From da7262792947e6f29cbef2853863bceb4f7c665b Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 1 Apr 2024 12:36:50 -0400 Subject: [PATCH 01/43] pqDisplayColorWidget: No need to cache representation, just check for null --- Qt/Components/pqDisplayColorWidget.cxx | 4 +--- Qt/Components/pqDisplayColorWidget.h | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Qt/Components/pqDisplayColorWidget.cxx b/Qt/Components/pqDisplayColorWidget.cxx index 8a7ec89ef9e..5d0b4fd1272 100644 --- a/Qt/Components/pqDisplayColorWidget.cxx +++ b/Qt/Components/pqDisplayColorWidget.cxx @@ -220,7 +220,6 @@ public: //----------------------------------------------------------------------------- pqDisplayColorWidget::pqDisplayColorWidget(QWidget* parentObject) : Superclass(parentObject) - , CachedRepresentation(nullptr) , Internals(new pqDisplayColorWidget::pqInternals()) { this->CellDataIcon = new QIcon(":/pqWidgets/Icons/pqCellData.svg"); @@ -292,7 +291,7 @@ vtkSMViewProxy* pqDisplayColorWidget::viewProxy() const //----------------------------------------------------------------------------- void pqDisplayColorWidget::setRepresentation(pqDataRepresentation* repr) { - if (this->CachedRepresentation == repr) + if (this->Representation != nullptr && this->Representation == repr) { return; } @@ -319,7 +318,6 @@ void pqDisplayColorWidget::setRepresentation(pqDataRepresentation* repr) this->Internals->OutOfDomainEntryIndex = -1; // now, save the new repr. - this->CachedRepresentation = repr; this->Representation = repr; vtkSMProxy* proxy = repr ? repr->getProxy() : nullptr; diff --git a/Qt/Components/pqDisplayColorWidget.h b/Qt/Components/pqDisplayColorWidget.h index 175b1278307..2afeebeec68 100644 --- a/Qt/Components/pqDisplayColorWidget.h +++ b/Qt/Components/pqDisplayColorWidget.h @@ -157,9 +157,6 @@ private: QPointer ColorTransferFunction; QString RepresentationText; - // This is maintained to detect when the representation has changed. - void* CachedRepresentation; - class pqInternals; pqInternals* Internals; -- GitLab From 78df83f0c9c4c49d81c6b41549bc4cd93e7fac2c Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 1 Apr 2024 14:00:03 -0400 Subject: [PATCH 02/43] pqMultiBlockInspectorWidget: No need to cache last port/representation --- Qt/Components/pqMultiBlockInspectorWidget.cxx | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/Qt/Components/pqMultiBlockInspectorWidget.cxx b/Qt/Components/pqMultiBlockInspectorWidget.cxx index c8bb9a73aee..483a0ec60ee 100644 --- a/Qt/Components/pqMultiBlockInspectorWidget.cxx +++ b/Qt/Components/pqMultiBlockInspectorWidget.cxx @@ -28,9 +28,6 @@ class pqMultiBlockInspectorWidget::pqInternals : public QObject QPointer OutputPort; QPointer HelperProxyWidget; - void* LastOutputPort = nullptr; - void* LastRepresentation = nullptr; - vtkSmartPointer HelperProxy; vtkSMProxy* helperProxy() @@ -74,13 +71,11 @@ public: Ui::MultiBlockInspectorWidget Ui; pqInternals(pqMultiBlockInspectorWidget* self) - : LastOutputPort(nullptr) - , LastRepresentation(nullptr) { this->Ui.setupUi(self); if (auto settings = pqApplicationCore::instance()->settings()) { - bool checked = settings->value("pqMultiBlockInspectorWidget/ShowHints", true).toBool(); + const bool checked = settings->value("pqMultiBlockInspectorWidget/ShowHints", true).toBool(); this->Ui.showHints->setChecked(checked); } } @@ -154,16 +149,8 @@ void pqMultiBlockInspectorWidget::pqInternals::update() auto port = this->outputPort(); auto repr = this->representation(); - if (port == this->LastOutputPort && repr == this->LastRepresentation) - { - // nothing has changed. - return; - } - delete this->HelperProxyWidget; - this->LastOutputPort = port; - this->LastRepresentation = repr; - if (!port) + if (!port || !repr) { return; } -- GitLab From 758e8d73a03b13b24252505b441908645f32bd6b Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Tue, 7 May 2024 15:10:00 -0400 Subject: [PATCH 03/43] vtkSMTrace: Add tracing for string vector with key --- Remoting/ServerManager/vtkSMTrace.cxx | 34 +++++++++++++++++++++++++++ Remoting/ServerManager/vtkSMTrace.h | 1 + 2 files changed, 35 insertions(+) diff --git a/Remoting/ServerManager/vtkSMTrace.cxx b/Remoting/ServerManager/vtkSMTrace.cxx index 5680aecb37a..a7d01f4ca78 100644 --- a/Remoting/ServerManager/vtkSMTrace.cxx +++ b/Remoting/ServerManager/vtkSMTrace.cxx @@ -461,6 +461,7 @@ vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg(const char* key, bool vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg( const char* key, const std::vector& val) { + assert(key); if (vtkSMTrace::GetActiveTracer()) { #if VTK_MODULE_ENABLE_VTK_PythonInterpreter && VTK_MODULE_ENABLE_VTK_Python && \ @@ -492,6 +493,7 @@ vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg( vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg( const char* key, const std::vector& val) { + assert(key); if (vtkSMTrace::GetActiveTracer()) { #if VTK_MODULE_ENABLE_VTK_PythonInterpreter && VTK_MODULE_ENABLE_VTK_Python && \ @@ -519,6 +521,38 @@ vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg( return *this; } +//---------------------------------------------------------------------------- +vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg( + const char* key, const std::vector& val) +{ + assert(key); + if (vtkSMTrace::GetActiveTracer()) + { +#if VTK_MODULE_ENABLE_VTK_PythonInterpreter && VTK_MODULE_ENABLE_VTK_Python && \ + VTK_MODULE_ENABLE_VTK_WrappingPythonCore + vtkPythonScopeGilEnsurer gilEnsurer; + vtkSmartPyObject keyObj(PyUnicode_FromString(key)); + vtkSmartPyObject listObj(PyList_New(0)); + assert(keyObj && listObj); + + for (const std::string& item : val) + { + vtkSmartPyObject valObj(PyUnicode_FromString(item.c_str())); + int ret = PyList_Append(listObj, valObj); + (void)ret; + assert(ret == 0); + } + + int ret = PyDict_SetItem(this->Internals->GetKWArgs(), keyObj, listObj); + (void)ret; + assert(ret == 0); +#endif + } + (void)key; + (void)val; + return *this; +} + //---------------------------------------------------------------------------- vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg(vtkObject* val) { diff --git a/Remoting/ServerManager/vtkSMTrace.h b/Remoting/ServerManager/vtkSMTrace.h index d476fbfaea6..897590682ff 100644 --- a/Remoting/ServerManager/vtkSMTrace.h +++ b/Remoting/ServerManager/vtkSMTrace.h @@ -180,6 +180,7 @@ public: TraceItemArgs& arg(const char* key, bool val); TraceItemArgs& arg(const char* key, const std::vector& val); TraceItemArgs& arg(const char* key, const std::vector& val); + TraceItemArgs& arg(const char* key, const std::vector& val); // Overloads for positional arguments. TraceItemArgs& arg(vtkObject* val); -- GitLab From c6092fee251f6eb8c84e0a2ed96b41c1ad7fd2dc Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 9 May 2024 10:12:30 -0400 Subject: [PATCH 04/43] vtkSMTrace: Add tracing for vtkObject vector with key --- Remoting/ServerManager/vtkSMTrace.cxx | 32 +++++++++++++++++++++++++++ Remoting/ServerManager/vtkSMTrace.h | 1 + 2 files changed, 33 insertions(+) diff --git a/Remoting/ServerManager/vtkSMTrace.cxx b/Remoting/ServerManager/vtkSMTrace.cxx index a7d01f4ca78..367e9087d57 100644 --- a/Remoting/ServerManager/vtkSMTrace.cxx +++ b/Remoting/ServerManager/vtkSMTrace.cxx @@ -553,6 +553,38 @@ vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg( return *this; } +//---------------------------------------------------------------------------- +vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg( + const char* key, const std::vector& val) +{ + assert(key); + if (vtkSMTrace::GetActiveTracer()) + { +#if VTK_MODULE_ENABLE_VTK_PythonInterpreter && VTK_MODULE_ENABLE_VTK_Python && \ + VTK_MODULE_ENABLE_VTK_WrappingPythonCore + vtkPythonScopeGilEnsurer gilEnsurer; + vtkSmartPyObject keyObj(PyUnicode_FromString(key)); + vtkSmartPyObject listObj(PyList_New(0)); + assert(keyObj && listObj); + + for (vtkObject* item : val) + { + vtkSmartPyObject valObj(vtkPythonUtil::GetObjectFromPointer(item)); + int ret = PyList_Append(listObj, valObj); + (void)ret; + assert(ret == 0); + } + + int ret = PyDict_SetItem(this->Internals->GetKWArgs(), keyObj, listObj); + (void)ret; + assert(ret == 0); +#endif + } + (void)key; + (void)val; + return *this; +} + //---------------------------------------------------------------------------- vtkSMTrace::TraceItemArgs& vtkSMTrace::TraceItemArgs::arg(vtkObject* val) { diff --git a/Remoting/ServerManager/vtkSMTrace.h b/Remoting/ServerManager/vtkSMTrace.h index 897590682ff..360d1dbacb3 100644 --- a/Remoting/ServerManager/vtkSMTrace.h +++ b/Remoting/ServerManager/vtkSMTrace.h @@ -181,6 +181,7 @@ public: TraceItemArgs& arg(const char* key, const std::vector& val); TraceItemArgs& arg(const char* key, const std::vector& val); TraceItemArgs& arg(const char* key, const std::vector& val); + TraceItemArgs& arg(const char* key, const std::vector& val); // Overloads for positional arguments. TraceItemArgs& arg(vtkObject* val); -- GitLab From 7df8f205cfd01a65b095f13a9930ccefb6f9aa6c Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 9 May 2024 10:12:59 -0400 Subject: [PATCH 05/43] vtkSMSourceProxy: Avoid accessing invalid output port index --- Remoting/ServerManager/vtkSMSourceProxy.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Remoting/ServerManager/vtkSMSourceProxy.cxx b/Remoting/ServerManager/vtkSMSourceProxy.cxx index 84644f812ad..0a15afe18bf 100644 --- a/Remoting/ServerManager/vtkSMSourceProxy.cxx +++ b/Remoting/ServerManager/vtkSMSourceProxy.cxx @@ -157,8 +157,9 @@ unsigned int vtkSMSourceProxy::GetNumberOfOutputPorts() //--------------------------------------------------------------------------- vtkSMOutputPort* vtkSMSourceProxy::GetOutputPort(unsigned int idx) { - return idx == VTK_UNSIGNED_INT_MAX ? nullptr - : this->PInternals->OutputPorts[idx].Port.GetPointer(); + return idx < this->PInternals->OutputPorts.size() + ? this->PInternals->OutputPorts[idx].Port.GetPointer() + : nullptr; } //--------------------------------------------------------------------------- -- GitLab From 5343196cdf21e878fe702e9e69637dc5000e2219 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 9 May 2024 10:13:20 -0400 Subject: [PATCH 06/43] vtkSMProperty: Improve documentation --- Remoting/ServerManager/vtkSMProperty.h | 48 +++++++------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/Remoting/ServerManager/vtkSMProperty.h b/Remoting/ServerManager/vtkSMProperty.h index e560b88ec90..6ccc5e6b04c 100644 --- a/Remoting/ServerManager/vtkSMProperty.h +++ b/Remoting/ServerManager/vtkSMProperty.h @@ -116,6 +116,13 @@ * trace generated by smtrace.py will not contain the properties of the * proxy pointed by the proxy property. * + * If you want to avoid having a default You can you use: + * + * @code{xml} + * + * + * + * @endcode */ #ifndef vtkSMProperty_h @@ -309,21 +316,15 @@ public: ///@{ /** - * Sets whether the property should ignore custom default settings. + * Set/Get whether the property should ignore custom default settings. */ vtkSetMacro(NoCustomDefault, int); - ///@} - - ///@{ - /** - * Gets whether the property should ignore custom default settings. - */ vtkGetMacro(NoCustomDefault, int); ///@} ///@{ /** - * Sets the panel visibility for the property. The value can be + * Set/Get the panel visibility for the property. The value can be * one of: * * "default": Show the property by default. * * "advanced": Only show the property in the advanced view. @@ -333,56 +334,31 @@ public: * while information_only properties have "never" visibility. */ vtkSetStringMacro(PanelVisibility); - ///@} - - ///@{ - /** - * Returns the panel visibility for the property. - */ vtkGetStringMacro(PanelVisibility); ///@} ///@{ /** - * Sets the panel visibility to default if the current + * Set/Get the panel visibility to default if the current * representation type matches \p representation. */ vtkSetStringMacro(PanelVisibilityDefaultForRepresentation); - ///@} - - ///@{ - /** - * Returns which representation type the property will be shown by - * default for. - */ vtkGetStringMacro(PanelVisibilityDefaultForRepresentation); ///@} ///@{ /** - * Sets the name of the custom panel widget to use for the property. + * Set/Get the name of the custom panel widget to use for the property. */ vtkSetStringMacro(PanelWidget); - ///@} - - ///@{ - /** - * Returns name of the panel widget for the property. - */ vtkGetStringMacro(PanelWidget); ///@} ///@{ /** - * Sets the tracing of sub property of this property + * Set/Get the tracing of sub property of this property */ vtkSetStringMacro(DisableSubTrace); - ///@} - - ///@{ - /** - * Returns the tracing state of the properties of this property - */ vtkGetStringMacro(DisableSubTrace); ///@} -- GitLab From 91590a3262f118b52aebe8ec5177f583b0560d2e Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 9 May 2024 10:56:01 -0400 Subject: [PATCH 07/43] vtkSMColorMapEditorHelper: Add functionality for all block properties Also remove the property of BlockLookupTableSelectors since we can extract the selectors from the BlockColorArrayNames. Also reorganize the order of the properties to be consistent with the GUI. --- Plugins/Prism/Views/PrismViews.xml | 9 +- Remoting/ServerManager/vtkSMOutputPort.cxx | 3 +- .../Resources/views_and_representations.xml | 368 +- Remoting/Views/vtkGeometryRepresentation.cxx | 70 +- Remoting/Views/vtkGeometryRepresentation.h | 21 +- Remoting/Views/vtkSMColorMapEditorHelper.cxx | 3427 ++++++++++++----- Remoting/Views/vtkSMColorMapEditorHelper.h | 608 ++- Remoting/Views/vtkSMPVRepresentationProxy.cxx | 215 +- Remoting/Views/vtkSMPVRepresentationProxy.h | 196 +- .../Views/vtkSMTransferFunctionManager.cxx | 14 +- 10 files changed, 3667 insertions(+), 1264 deletions(-) diff --git a/Plugins/Prism/Views/PrismViews.xml b/Plugins/Prism/Views/PrismViews.xml index f01cae83fb3..2e3232e9163 100644 --- a/Plugins/Prism/Views/PrismViews.xml +++ b/Plugins/Prism/Views/PrismViews.xml @@ -1119,15 +1119,8 @@ panel_widget="DataAssemblyEditor" panel_visibility="multiblock_inspector"> + - - - - - - - - diff --git a/Remoting/ServerManager/vtkSMOutputPort.cxx b/Remoting/ServerManager/vtkSMOutputPort.cxx index 95bc5ca7a2b..a0e51ff4fa3 100644 --- a/Remoting/ServerManager/vtkSMOutputPort.cxx +++ b/Remoting/ServerManager/vtkSMOutputPort.cxx @@ -125,7 +125,7 @@ vtkPVDataInformation* vtkSMOutputPort::GetSubsetDataInformation( vtkPVTemporalDataInformation* vtkSMOutputPort::GetTemporalSubsetDataInformation( const char* selector, const char* assemblyName) { - auto dinfo = this->GetDataInformation(); + auto dinfo = this->GetTemporalDataInformation(); auto assembly = dinfo->GetDataAssembly(assemblyName); if (assembly == nullptr || selector == nullptr || selector[0] == '\0') { @@ -251,6 +251,7 @@ void vtkSMOutputPort::InvalidateDataInformation() this->TemporalDataInformationValid = false; this->SubsetDataInformations.clear(); this->RankDataInformations.clear(); + this->TemporalSubsetDataInformations.clear(); } //---------------------------------------------------------------------------- diff --git a/Remoting/Views/Resources/views_and_representations.xml b/Remoting/Views/Resources/views_and_representations.xml index c937cc12f8e..3cf856aa9ac 100644 --- a/Remoting/Views/Resources/views_and_representations.xml +++ b/Remoting/Views/Resources/views_and_representations.xml @@ -4706,15 +4706,8 @@ OSPRay pathtracer may need to transfer default materials from client to server. panel_widget="DataAssemblyEditor" panel_visibility="multiblock_inspector"> + - - - - - - - - @@ -5121,13 +5114,9 @@ OSPRay pathtracer may need to transfer default materials from client to server. - - - - + + + @@ -7834,13 +7823,13 @@ OSPRay pathtracer may need to transfer default materials from client to server. + command="AddBlockSelector" + clean_command="RemoveAllBlockSelectors" + repeat_command="1" + number_of_elements_per_command="1" + panel_visibility="multiblock_inspector" + number_of_elements="1" + default_values="/"> @@ -8651,7 +8640,6 @@ OSPRay pathtracer may need to transfer default materials from client to server. @@ -8664,14 +8652,33 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + + + + + + + + + + Used by the multiblock inspector to track the selected block selectors. + + + + command="AddBlockSelector" + clean_command="RemoveAllBlockSelectors" + repeat_command="1" + number_of_elements_per_command="1" + panel_visibility="multiblock_inspector" + number_of_elements="1" + default_values="/"> @@ -8697,12 +8704,120 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + For composite datasets, specify colors associated with selectors on the assembly chosen using **Assembly**. + + + + + + + + + + For composite datasets, specify the color array name associated with selectors + on the assembly chosen using **Assembly**. + + + + + + + + + + + + + + + Set the block lookup-table to use to map data array to colors. + Lookup table is only used with MapScalars to ON or BlockMapScalars to ON. + + + + + + + + + + + + + + + By default, ParaView shares a single color map among all datasets colored + by arrays that have the same name. When this option is enabled, the block of the active dataset will + be assigned a separate color map not shared by other blocks or datasets. + + + + + + + + + + + + + + + For composite datasets, specify if to map scalars associated with selectors + on the assembly chosen using **Assembly**. + + + + + + + + + + + + + + + For composite datasets, specify if to interpolate scalars before mapping associated with selectors + on the assembly chosen using **Assembly**. + + + + + + For composite datasets, specify opacities associated with selectors on the assembly chosen using **Assembly**. - - - - - - - - - - > + input_domain_name="input_array_any"> @@ -16024,14 +16130,33 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + + + + + + + + + + Used by the multiblock inspector to track the selected block selectors. + + + + command="AddBlockSelector" + clean_command="RemoveAllBlockSelectors" + repeat_command="1" + number_of_elements_per_command="1" + panel_visibility="multiblock_inspector" + number_of_elements="1" + default_values="/"> @@ -16057,34 +16182,56 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + For composite datasets, specify colors associated with selectors on the assembly chosen using **Assembly**. - - + - - + + + - + - For composite datasets, specify opacities associated with selectors + For composite datasets, specify the color array name associated with selectors on the assembly chosen using **Assembly**. + + + - + + + + + + + + Set the block lookup-table to use to map data array to colors. + Lookup table is only used with MapScalars to ON or BlockMapScalars to ON. + + + + + + + - For composite datasets, specify if to interpolate scalars before mapping associated with selectors - on the assembly chosen using **Assembly**. + By default, ParaView shares a single color map among all datasets colored + by arrays that have the same name. When this option is enabled, the block of the active dataset will + be assigned a separate color map not shared by other blocks or datasets. @@ -16114,17 +16265,20 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + For composite datasets, specify if to map scalars associated with selectors on the assembly chosen using **Assembly**. - @@ -16133,79 +16287,37 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + - For composite datasets, specify the color array name associated with selectors + For composite datasets, specify if to interpolate scalars before mapping associated with selectors on the assembly chosen using **Assembly**. - - - - - - - - - For composite datasets, specify the selectors for the look up tables on the assembly chosen using - **Assembly**. - - - - - Set the block lookup-table to use to map data array to colors. - Lookuptable is only used with MapScalars to ON or BlockMapScalars to ON. - - - - - - - - + - - + + + + + - By default, ParaView shares a single color map among all datasets colored - by arrays that have the same name. When this option is enabled, the block of the active dataset will - be assigned a separate color map not shared by other blocks or datasets. + For composite datasets, specify opacities associated with selectors + on the assembly chosen using **Assembly**. - - - - - - - - - - - - - - - - #include +#include #include #include #include @@ -1473,8 +1474,11 @@ void vtkGeometryRepresentation::SetFeatureAngle(double val) //---------------------------------------------------------------------------- void vtkGeometryRepresentation::AddBlockSelector(const char* selector) { - if (selector != nullptr && this->BlockSelectors.insert(selector).second) + if (selector != nullptr && + std::find(this->BlockSelectors.begin(), this->BlockSelectors.end(), selector) == + this->BlockSelectors.end()) { + this->BlockSelectors.push_back(selector); this->BlockAttrChanged = true; } } @@ -1494,10 +1498,12 @@ void vtkGeometryRepresentation::SetBlockColor(const char* selector, double r, do { if (selector != nullptr) { - auto iter = this->BlockColors.find(selector); + auto iter = std::find_if(this->BlockColors.begin(), this->BlockColors.end(), + [selector]( + const std::pair& apair) { return apair.first == selector; }); if (iter == this->BlockColors.end()) { - this->BlockColors.insert(std::make_pair(selector, vtkVector3d(r, g, b))); + this->BlockColors.emplace_back(selector, vtkVector3d(r, g, b)); this->BlockAttrChanged = true; } else if (iter->second != vtkVector3d(r, g, b)) @@ -1523,10 +1529,11 @@ void vtkGeometryRepresentation::SetBlockOpacity(const char* selector, double alp { if (selector != nullptr) { - auto iter = this->BlockOpacities.find(selector); + auto iter = std::find_if(this->BlockOpacities.begin(), this->BlockOpacities.end(), + [selector](const std::pair& apair) { return apair.first == selector; }); if (iter == this->BlockOpacities.end()) { - this->BlockOpacities.insert(std::make_pair(selector, alpha)); + this->BlockOpacities.emplace_back(selector, alpha); this->BlockAttrChanged = true; } else if (iter->second != alpha) @@ -1553,10 +1560,12 @@ void vtkGeometryRepresentation::SetBlockInterpolateScalarsBeforeMapping( { if (selector != nullptr) { - auto iter = this->BlockInterpolateScalarsBeforeMapping.find(selector); + auto iter = std::find_if(this->BlockInterpolateScalarsBeforeMapping.begin(), + this->BlockInterpolateScalarsBeforeMapping.end(), + [selector](const std::pair& apair) { return apair.first == selector; }); if (iter == this->BlockInterpolateScalarsBeforeMapping.end()) { - this->BlockInterpolateScalarsBeforeMapping.insert(std::make_pair(selector, interpolate)); + this->BlockInterpolateScalarsBeforeMapping.emplace_back(selector, interpolate); this->BlockAttrChanged = true; } else if (iter->second != interpolate) @@ -1590,10 +1599,11 @@ void vtkGeometryRepresentation::SetBlockMapScalars(const char* selector, int val const int colorMode = mapToColorMode[val]; if (selector != nullptr) { - auto iter = this->BlockColorModes.find(selector); + auto iter = std::find_if(this->BlockColorModes.begin(), this->BlockColorModes.end(), + [selector](const std::pair& apair) { return apair.first == selector; }); if (iter == this->BlockColorModes.end()) { - this->BlockColorModes.insert(std::make_pair(selector, colorMode)); + this->BlockColorModes.emplace_back(selector, colorMode); this->BlockAttrChanged = true; } else if (iter->second != colorMode) @@ -1620,10 +1630,13 @@ void vtkGeometryRepresentation::SetBlockArrayName( { if (selector != nullptr && colorArray != nullptr) { - auto iter = this->BlockArrayNames.find(selector); + auto iter = std::find_if(this->BlockArrayNames.begin(), this->BlockArrayNames.end(), + [selector](const std::pair>& apair) { + return apair.first == selector; + }); if (iter == this->BlockArrayNames.end()) { - this->BlockArrayNames.insert(std::make_pair(selector, std::make_pair(assoc, colorArray))); + this->BlockArrayNames.emplace_back(selector, std::make_pair(assoc, colorArray)); this->BlockAttrChanged = true; } else if (iter->second.first != assoc && iter->second.second != colorArray) @@ -1644,26 +1657,6 @@ void vtkGeometryRepresentation::RemoveAllBlockArrayNames() } } -//---------------------------------------------------------------------------- -void vtkGeometryRepresentation::SetBlockLookupTableSelector(const char* selector) -{ - if (selector != nullptr) - { - this->BlockLookupTableSelectors.push_back(selector); - this->BlockAttrChanged = true; - } -} - -//---------------------------------------------------------------------------- -void vtkGeometryRepresentation::RemoveAllBlockLookupTableSelectors() -{ - if (!this->BlockLookupTableSelectors.empty()) - { - this->BlockLookupTableSelectors.clear(); - this->BlockAttrChanged = true; - } -} - //---------------------------------------------------------------------------- void vtkGeometryRepresentation::SetBlockLookupTable(vtkScalarsToColors* lut) { @@ -1723,9 +1716,6 @@ void vtkGeometryRepresentation::PopulateBlockAttributes( // Handle visibilities. attrs->SetBlockVisibility(dtree, false); // start by marking root invisible first. - // create a vector of selectors for block visibilities - const std::vector blockVisibilitySelectors( - this->BlockSelectors.begin(), this->BlockSelectors.end()); // get the selectors for block properties std::set blockPropertiesSelectorsSet; for (const auto& item : this->BlockColors) @@ -1748,10 +1738,6 @@ void vtkGeometryRepresentation::PopulateBlockAttributes( { blockPropertiesSelectorsSet.emplace(item.first); } - for (const auto& item : this->BlockLookupTableSelectors) - { - blockPropertiesSelectorsSet.emplace(item); - } // create a vector of selectors for block properties const std::vector blockPropertiesSelectors( @@ -1767,7 +1753,7 @@ void vtkGeometryRepresentation::PopulateBlockAttributes( { // we need to convert assembly selectors to composite ids. cids = vtkDataAssemblyUtilities::GetSelectedCompositeIds( - blockVisibilitySelectors, outputPDC->GetDataAssembly(), outputPDC); + this->BlockSelectors, outputPDC->GetDataAssembly(), outputPDC); // create a composite ids map for the assembly selectors. for (const auto& selector : blockPropertiesSelectors) @@ -1785,7 +1771,7 @@ void vtkGeometryRepresentation::PopulateBlockAttributes( return; } // compute the composite ids for the hierarchy selectors - cids = vtkDataAssemblyUtilities::GetSelectedCompositeIds(blockVisibilitySelectors, hierarchy); + cids = vtkDataAssemblyUtilities::GetSelectedCompositeIds(this->BlockSelectors, hierarchy); // create a composite ids map for the hierarchy selectors. for (const auto& selector : blockPropertiesSelectors) { @@ -1895,11 +1881,11 @@ void vtkGeometryRepresentation::PopulateBlockAttributes( } // Handle lookup tables - if (this->BlockLookupTables.size() == this->BlockLookupTableSelectors.size()) + if (this->BlockLookupTables.size() == this->BlockArrayNames.size()) { for (size_t i = 0, numLUTs = this->BlockLookupTables.size(); i < numLUTs; ++i) { - const auto& ids = selectorsCids[this->BlockLookupTableSelectors[i]]; + const auto& ids = selectorsCids[this->BlockArrayNames[i].first]; for (const auto& id : ids) { auto iter = cid_to_dobj.find(id); diff --git a/Remoting/Views/vtkGeometryRepresentation.h b/Remoting/Views/vtkGeometryRepresentation.h index f7ae2dea5cb..9d152b00782 100644 --- a/Remoting/Views/vtkGeometryRepresentation.h +++ b/Remoting/Views/vtkGeometryRepresentation.h @@ -343,8 +343,6 @@ public: * Note: we need two methods because we can't wrap a string and a vtkObject using either * vtkSMStringVectorProperty or vtkSMProxyProperty. */ - void SetBlockLookupTableSelector(const char* selector); - void RemoveAllBlockLookupTableSelectors(); void SetBlockLookupTable(vtkScalarsToColors* lut); void RemoveAllBlockLookupTables(); ///@} @@ -574,22 +572,21 @@ protected: /** * Configurable through vtkGeometryRepresentation API */ - std::set BlockSelectors; - std::unordered_map BlockColors; - std::unordered_map BlockOpacities; - std::unordered_map BlockInterpolateScalarsBeforeMapping; - std::unordered_map BlockColorModes; - std::unordered_map> BlockArrayNames; - std::vector BlockLookupTableSelectors; + std::vector BlockSelectors; + std::vector> BlockColors; + std::vector> BlockOpacities; + std::vector> BlockInterpolateScalarsBeforeMapping; + std::vector> BlockColorModes; + std::vector>> BlockArrayNames; std::vector BlockLookupTables; ///@} ///@{ /** * Configured internally in vtkGeometryRepresentation */ - std::unordered_map BlockScalarVisibilities; - std::unordered_map BlockUseLookupTableScalarRanges; - std::unordered_map BlockFieldDataTupleIds; + std::vector> BlockScalarVisibilities; + std::vector> BlockUseLookupTableScalarRanges; + std::vector> BlockFieldDataTupleIds; ///@} private: bool DisableLighting = false; diff --git a/Remoting/Views/vtkSMColorMapEditorHelper.cxx b/Remoting/Views/vtkSMColorMapEditorHelper.cxx index aac49a95b65..1e10abcd1f2 100644 --- a/Remoting/Views/vtkSMColorMapEditorHelper.cxx +++ b/Remoting/Views/vtkSMColorMapEditorHelper.cxx @@ -5,6 +5,7 @@ #include "vtkDataObject.h" #include "vtkDoubleArray.h" #include "vtkNew.h" +#include "vtkObjectFactory.h" #include "vtkPVArrayInformation.h" #include "vtkPVDataInformation.h" #include "vtkPVProminentValuesInformation.h" @@ -26,8 +27,228 @@ #include "vtkSMTransferFunctionManager.h" #include "vtkSMTransferFunctionProxy.h" #include "vtkStringList.h" +#include "vtkVariant.h" +#include "vtksys/SystemTools.hxx" + +#include +#include +#include +#include +#include #include +#include +#include + +//---------------------------------------------------------------------------- +vtkStandardNewMacro(vtkSMColorMapEditorHelper); + +//---------------------------------------------------------------------------- +vtkSMColorMapEditorHelper::vtkSMColorMapEditorHelper() = default; + +//---------------------------------------------------------------------------- +vtkSMColorMapEditorHelper::~vtkSMColorMapEditorHelper() = default; + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + os << indent + << "SelectedPropertiesType: " << (this->SelectedPropertiesType ? "Blocks" : "Representation") + << "\n"; +} + +//---------------------------------------------------------------------------- +vtkSMColorMapEditorHelper::SelectedPropertiesTypes vtkSMColorMapEditorHelper::GetPropertyType( + vtkSMProperty* prop) +{ + if (!prop) + { + return SelectedPropertiesTypes::Representation; + } + return std::string(prop->GetXMLName()).substr(0, 5) == "Block" + ? SelectedPropertiesTypes::Blocks + : SelectedPropertiesTypes::Representation; +} + +namespace +{ +std::vector GetItSelfAndParents(const std::string& blockSelector) +{ + std::vector parts = vtksys::SystemTools::SplitString(blockSelector, '/', true); + std::vector paths; + std::stringstream ss; + for (size_t i = 1; i < parts.size(); ++i) + { + ss << "/" + parts[i]; + paths.push_back(ss.str()); + } + // reverse to get the block selector first + std::reverse(paths.begin(), paths.end()); + return paths; +} +} + +//---------------------------------------------------------------------------- +std::pair +vtkSMColorMapEditorHelper::HasBlockProperty( + vtkSMProxy* proxy, const std::string& blockSelector, const std::string& propertyName) +{ + if (blockSelector.empty()) + { + return { "/", BlockPropertyState::Disabled }; + } + const auto blockProperty = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty(propertyName.c_str())); + if (!blockProperty) + { + return { "/", BlockPropertyState::RepresentationInherited }; + } + const std::vector paths = ::GetItSelfAndParents(blockSelector); + const int nEpC = blockProperty->GetNumberOfElementsPerCommand(); + for (size_t i = 0; i < paths.size(); ++i) + { + // the first path is the blockSelector itself + const BlockPropertyState state = + i == 0 ? BlockPropertyState::Set : BlockPropertyState::BlockInherited; + for (unsigned int j = 0; j < blockProperty->GetNumberOfElements(); j += nEpC) + { + if (blockProperty->GetElement(j) == paths[i]) + { + return { paths[i], state }; + } + } + } + return { "/", BlockPropertyState::RepresentationInherited }; +} + +//---------------------------------------------------------------------------- +std::pair +vtkSMColorMapEditorHelper::GetBlockPropertyStateFromBlockPropertyStates( + const std::vector>& blockPropertyStates) +{ + std::string path = "/"; + std::uint8_t state = BlockPropertyState::Disabled; + for (const std::pair& blockPropertyState : blockPropertyStates) + { + if (blockPropertyState.second > state) + { + path = blockPropertyState.first; + } + state |= static_cast(blockPropertyState.second); + } + return std::make_pair(path, static_cast(state)); +} + +//---------------------------------------------------------------------------- +std::pair +vtkSMColorMapEditorHelper::HasBlockProperty(vtkSMProxy* proxy, + const std::vector& blockSelectors, const std::string& propertyName) +{ + if (blockSelectors.empty()) + { + return { "/", BlockPropertyState::Disabled }; + } + std::vector> blockPropertyStates; + for (const std::string& blockSelector : blockSelectors) + { + blockPropertyStates.push_back( + vtkSMColorMapEditorHelper::HasBlockProperty(proxy, blockSelector, propertyName)); + } + return vtkSMColorMapEditorHelper::GetBlockPropertyStateFromBlockPropertyStates( + blockPropertyStates); +} + +//---------------------------------------------------------------------------- +std::pair +vtkSMColorMapEditorHelper::HasBlockProperties(vtkSMProxy* proxy, const std::string& blockSelector, + const std::vector& propertyNames) +{ + std::vector> blockPropertyStates; + for (const std::string& propertyName : propertyNames) + { + blockPropertyStates.push_back( + vtkSMColorMapEditorHelper::HasBlockProperty(proxy, blockSelector, propertyName)); + } + return vtkSMColorMapEditorHelper::GetBlockPropertyStateFromBlockPropertyStates( + blockPropertyStates); +} + +//---------------------------------------------------------------------------- +std::pair +vtkSMColorMapEditorHelper::HasBlocksProperties(vtkSMProxy* proxy, + const std::vector& blockSelectors, const std::vector& propertyNames) +{ + if (blockSelectors.empty()) + { + return { "/", BlockPropertyState::Disabled }; + } + std::vector> blockPropertyStates; + for (const std::string blockSelector : blockSelectors) + { + blockPropertyStates.push_back( + vtkSMColorMapEditorHelper::HasBlockProperties(proxy, blockSelector, propertyNames)); + } + return vtkSMColorMapEditorHelper::GetBlockPropertyStateFromBlockPropertyStates( + blockPropertyStates); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(vtkSMProxy* proxy) +{ + const auto selectedBlockSelectorsProp = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("SelectedBlockSelectors")); + if (selectedBlockSelectorsProp) + { + const std::vector& elements = selectedBlockSelectorsProp->GetElements(); + if (elements.size() == 1) + { + return elements[0].empty() ? std::vector() : elements; + } + else + { + return elements; + } + } + else + { + return std::vector(); + } +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetColorArraysBlockSelectors(vtkSMProxy* proxy) +{ + const auto blockColorArray = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); + if (!blockColorArray) + { + vtkDebugWithObjectMacro(proxy, "No `BlockColorArrayNames` property found."); + return std::vector(); + } + std::vector blockSelectors; + for (unsigned int i = 0; i < blockColorArray->GetNumberOfElements(); i += 3) + { + const std::string blockSelector = blockColorArray->GetElement(i); + const std::string attributeTypeStr = blockColorArray->GetElement(i + 1); + const std::string arrayName = blockColorArray->GetElement(i + 2); + try + { + const int attributeType = std::stoi(attributeTypeStr); + if (vtkSMColorMapEditorHelper::IsColorArrayValid(std::make_pair(attributeType, arrayName))) + { + blockSelectors.push_back(blockSelector); + } + } + catch (const std::invalid_argument& e) + { + (void)e; + vtkDebugWithObjectMacro(proxy, "Invalid attribute type: " << attributeTypeStr << e.what()); + break; + } + } + return blockSelectors; +} //---------------------------------------------------------------------------- bool vtkSMColorMapEditorHelper::GetUsingScalarColoring(vtkSMProxy* proxy) @@ -47,18 +268,25 @@ bool vtkSMColorMapEditorHelper::GetUsingScalarColoring(vtkSMProxy* proxy) } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring( - vtkSMProxy* proxy, const std::string& blockSelector) +std::vector vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring( + vtkSMProxy* proxy, const std::vector& blockSelectors) { + if (blockSelectors.empty()) + { + return std::vector(); + } if (!vtkSMRepresentationProxy::SafeDownCast(proxy)) { - return false; + return std::vector(blockSelectors.size(), false); } - - const auto blockArrayAttributeTypeAndName = - vtkSMColorMapEditorHelper::GetBlockColorArray(proxy, blockSelector); - return blockArrayAttributeTypeAndName.first != -1 && - !blockArrayAttributeTypeAndName.second.empty(); + const std::vector blockColorArrays = + vtkSMColorMapEditorHelper::GetBlocksColorArrays(proxy, blockSelectors); + std::vector useScalarColorings(blockSelectors.size()); + for (size_t i = 0; i < blockSelectors.size(); ++i) + { + useScalarColorings[i] = vtkSMColorMapEditorHelper::IsColorArrayValid(blockColorArrays[i]); + } + return useScalarColorings; } //---------------------------------------------------------------------------- @@ -68,21 +296,35 @@ bool vtkSMColorMapEditorHelper::GetAnyBlockUsingScalarColoring(vtkSMProxy* proxy { return false; } - auto blockColorArray = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); - if (!blockColorArray) + const std::vector blockUsingScalarColoring = + vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring( + proxy, vtkSMColorMapEditorHelper::GetColorArraysBlockSelectors(proxy)); + return std::any_of(blockUsingScalarColoring.begin(), blockUsingScalarColoring.end(), + [](vtkTypeBool b) { return b; }); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedUsingScalarColorings( + vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) { - return false; + return vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); } - for (unsigned int i = 0; i < blockColorArray->GetNumberOfElements(); i += 3) + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation { - if (vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring( - proxy, blockColorArray->GetElement(i))) - { - return true; - } + return { vtkSMColorMapEditorHelper::GetUsingScalarColoring(proxy) }; } - return false; +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::GetAnySelectedUsingScalarColoring(vtkSMProxy* proxy) +{ + const std::vector useScalarColorings = + vtkSMColorMapEditorHelper::GetSelectedUsingScalarColorings(proxy); + return std::any_of( + useScalarColorings.begin(), useScalarColorings.end(), [](vtkTypeBool b) { return b; }); } //---------------------------------------------------------------------------- @@ -105,11 +347,11 @@ void vtkSMColorMapEditorHelper::SetupLookupTable(vtkSMProxy* proxy) } if (vtkSMProperty* lutProperty = proxy->GetProperty("LookupTable")) { - vtkSMTransferFunctionProxy* lutProxy = vtkSMTransferFunctionProxy::SafeDownCast( + vtkSMProxy* lut = vtkSMTransferFunctionProxy::SafeDownCast( mgr->GetColorTransferFunction(arrayName, proxy->GetSessionProxyManager())); const int rescaleMode = - vtkSMPropertyHelper(lutProxy, "AutomaticRescaleRangeMode", true).GetAsInt(); - vtkSMPropertyHelper(lutProperty).Set(lutProxy); + vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode", true).GetAsInt(); + vtkSMPropertyHelper(lutProperty).Set(lut); const bool extend = rescaleMode == vtkSMTransferFunctionManager::GROW_ON_APPLY; const bool force = false; vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange(proxy, extend, force); @@ -120,35 +362,43 @@ void vtkSMColorMapEditorHelper::SetupLookupTable(vtkSMProxy* proxy) } //---------------------------------------------------------------------------- -void vtkSMColorMapEditorHelper::SetupBlockLookupTable( - vtkSMProxy* proxy, const std::string& blockSelector) +void vtkSMColorMapEditorHelper::SetupBlocksLookupTables(vtkSMProxy* proxy) { - if (vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector)) + if (vtkSMColorMapEditorHelper::GetAnyBlockUsingScalarColoring(proxy)) { // If representation has been initialized to use scalar coloring and no // transfer functions are setup, we setup the transfer functions. const auto repr = vtkSMPVRepresentationProxy::SafeDownCast(proxy); - const auto blockArrayAttributeTypeAndName = - vtkSMColorMapEditorHelper::GetBlockColorArray(repr, blockSelector); - const auto arrayName = blockArrayAttributeTypeAndName.second; - if (!arrayName.empty()) + // get the selectors of the colored blocks + const std::vector blockSelectors = + vtkSMColorMapEditorHelper::GetColorArraysBlockSelectors(proxy); + const bool hasBlockLUT = repr->GetProperty("BlockLookupTables"); + // if the representation has a block lookup table property + if (!hasBlockLUT) { - vtkNew mgr; - const bool hasBlockLUT = - repr->GetProperty("BlockLookupTableSelectors") && repr->GetProperty("BlockLookupTables"); - if (hasBlockLUT) + return; + } + const std::map> commonColorArraysBlockSelectors = + vtkSMColorMapEditorHelper::GetCommonColorArraysBlockSelectors(proxy, blockSelectors); + vtkNew mgr; + for (const auto& commonColorArrayBlockSelectors : commonColorArraysBlockSelectors) + { + if (!vtkSMColorMapEditorHelper::IsColorArrayValid(commonColorArrayBlockSelectors.first)) { - auto lutProxy = vtkSMTransferFunctionProxy::SafeDownCast( - mgr->GetColorTransferFunction(arrayName.c_str(), proxy->GetSessionProxyManager())); - const int rescaleMode = - vtkSMPropertyHelper(lutProxy, "AutomaticRescaleRangeMode", true).GetAsInt(); - vtkSMColorMapEditorHelper::SetBlockLookupTable(proxy, blockSelector, lutProxy); - const bool extend = rescaleMode == vtkSMTransferFunctionManager::GROW_ON_APPLY; - const bool force = false; - vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange( - proxy, blockSelector, extend, force); - proxy->UpdateVTKObjects(); + return; } + const std::string arrayName = commonColorArrayBlockSelectors.first.second; + const std::vector& commonBlockSelectors = commonColorArrayBlockSelectors.second; + vtkSMProxy* lut = vtkSMTransferFunctionProxy::SafeDownCast( + mgr->GetColorTransferFunction(arrayName.c_str(), proxy->GetSessionProxyManager())); + const int rescaleMode = + vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode", true).GetAsInt(); + vtkSMColorMapEditorHelper::SetBlocksLookupTable(proxy, commonBlockSelectors, lut); + const bool extend = rescaleMode == vtkSMTransferFunctionManager::GROW_ON_APPLY; + const bool force = false; + vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + proxy, commonBlockSelectors, extend, force); + proxy->UpdateVTKObjects(); } } } @@ -201,58 +451,96 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange( - vtkSMProxy* proxy, const std::string& blockSelector, bool extend, bool force) +std::vector vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + vtkSMProxy* proxy, const std::vector& blockSelectors, bool extend, bool force) { - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector)) + if (blockSelectors.empty()) { - // we are not using scalar coloring, nothing to do. - return false; + return std::vector(); } - SM_SCOPED_TRACE(CallMethod) .arg(proxy) - .arg("RescaleBlockTransferFunctionToDataRange") - .arg(blockSelector.c_str()) + .arg("RescaleBlocksTransferFunctionToDataRange") + .arg(blockSelectors) .arg(extend) .arg(force) .arg("comment", - (extend - ? "rescale block color and/or opacity maps used to include current data range" - : "rescale block color and/or opacity maps used to exactly fit the current data range")); - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange(proxy, blockSelector, - vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray(proxy, blockSelector), extend, - force); + (extend ? "rescale block(s) color and/or opacity maps used to include current data range" + : "rescale block(s) color and/or opacity maps used to exactly fit the current data " + "range")); + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange(proxy, blockSelectors, + vtkSMColorMapEditorHelper::GetBlocksArrayInformationForColorArray(proxy, blockSelectors), + extend, force); } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange(vtkSMProxy* proxy, - const std::string& blockSelector, const char* arrayName, int attributeType, bool extend, - bool force) +std::vector vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + vtkSMProxy* proxy, const std::vector& blockSelectors, const char* arrayName, + int attributeType, bool extend, bool force) { - auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); const vtkSMPropertyHelper inputHelper(proxy->GetProperty("Input")); vtkSMSourceProxy* inputProxy = vtkSMSourceProxy::SafeDownCast(inputHelper.GetAsProxy()); const int port = inputHelper.GetOutputPort(); if (!inputProxy || !inputProxy->GetOutputPort(port)) { // no input. - vtkWarningWithObjectMacro(proxy, "No input present. Cannot determine data ranges."); - return false; + vtkDebugWithObjectMacro(proxy, "No input present. Cannot determine data ranges."); + return std::vector(blockSelectors.size(), false); } - vtkPVDataInformation* blockDataInfo = inputProxy->GetOutputPort(port)->GetSubsetDataInformation( - blockSelector.c_str(), vtkSMPropertyHelper(proxy, "Assembly", true).GetAsString()); - vtkPVArrayInformation* blockArrayInfo = - blockDataInfo->GetArrayInformation(arrayName, attributeType); - if (!blockArrayInfo) + std::vector arrayInfos(blockSelectors.size(), nullptr); + for (size_t i = 0; i < blockSelectors.size(); ++i) { - vtkPVDataInformation* representedDataInfo = repr->GetRepresentedDataInformation(); - blockArrayInfo = representedDataInfo->GetArrayInformation(arrayName, attributeType); + vtkPVDataInformation* blockDataInfo = inputProxy->GetOutputPort(port)->GetSubsetDataInformation( + blockSelectors[i].c_str(), vtkSMPropertyHelper(proxy, "Assembly", true).GetAsString()); + vtkPVArrayInformation* blockArrayInfo = + blockDataInfo->GetArrayInformation(arrayName, attributeType); + if (!blockArrayInfo) + { + vtkPVDataInformation* representedDataInfo = repr->GetRepresentedDataInformation(); + blockArrayInfo = representedDataInfo->GetArrayInformation(arrayName, attributeType); + } + arrayInfos[i] = blockArrayInfo; + } + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + repr, blockSelectors, arrayInfos, extend, force); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::RescaleSelectedTransferFunctionToDataRange( + vtkSMProxy* proxy, bool extend, bool force) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), extend, force); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange(proxy, extend, force) }; } +} - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange( - repr, blockSelector, blockArrayInfo, extend, force); +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::RescaleSelectedTransferFunctionToDataRange( + vtkSMProxy* proxy, const char* arrayName, int attributeType, bool extend, bool force) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange(proxy, + vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), arrayName, attributeType, extend, + force); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( + proxy, arrayName, attributeType, extend, force) }; + } } //---------------------------------------------------------------------------- @@ -281,47 +569,126 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRangeOverTime( vtkPVTemporalDataInformation* dataInfo = inputProxy->GetOutputPort(port)->GetTemporalDataInformation(); vtkPVArrayInformation* info = dataInfo->GetArrayInformation(arrayName, attributeType); - return info ? RescaleTransferFunctionToDataRange(proxy, info) : false; + return info ? vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange(proxy, info) : false; } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRangeOverTime( - vtkSMProxy* proxy, const std::string& blockSelector) +std::vector +vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + vtkSMProxy* proxy, const std::vector& blockSelectors) { - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector)) + if (blockSelectors.empty()) { - // we are not using scalar coloring, nothing to do. - return false; + return std::vector(); } + const std::map> commonColorArraysBlockSelectors = + vtkSMColorMapEditorHelper::GetCommonColorArraysBlockSelectors(proxy, blockSelectors); - const auto blockArrayAttributeTypeAndName = - vtkSMColorMapEditorHelper::GetBlockColorArray(proxy, blockSelector); - return vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRangeOverTime( - proxy, blockArrayAttributeTypeAndName.second.c_str(), blockArrayAttributeTypeAndName.first); + std::map> arrayResults; + for (const auto& commonColorArrayBlockSelectors : commonColorArraysBlockSelectors) + { + if (!vtkSMColorMapEditorHelper::IsColorArrayValid(commonColorArrayBlockSelectors.first)) + { + return std::vector(commonColorArrayBlockSelectors.second.size(), false); + } + const std::string arrayName = commonColorArrayBlockSelectors.first.second; + const std::vector& commonBlockSelectors = commonColorArrayBlockSelectors.second; + arrayResults[commonColorArrayBlockSelectors.first] = + vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + proxy, commonBlockSelectors, arrayName.c_str(), commonColorArrayBlockSelectors.first.first); + } + if (arrayResults.size() == 1) + { + return arrayResults.begin()->second; + } + else + { + const std::vector blocksColorArrays = + vtkSMColorMapEditorHelper::GetBlocksColorArrays(proxy, blockSelectors); + std::vector results(blockSelectors.size(), false); + for (size_t i = 0; i < blockSelectors.size(); ++i) + { + const ColorArray& blockColorArray = blocksColorArrays[i]; + const std::string selector = blockSelectors[i]; + const std::vector& colorArrayResults = arrayResults.find(blockColorArray)->second; + const std::vector& colorArrayBlockSelectors = + commonColorArraysBlockSelectors.find(blockColorArray)->second; + for (size_t j = 0; j < colorArrayResults.size(); ++j) + { + if (colorArrayBlockSelectors[j] == selector) + { + results[i] = colorArrayResults[j]; + break; + } + } + } + return results; + } } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRangeOverTime( - vtkSMProxy* proxy, const std::string& blockSelector, const char* arrayName, int attributeType) +std::vector +vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime(vtkSMProxy* proxy, + const std::vector& blockSelectors, const char* arrayName, int attributeType) { + if (blockSelectors.empty()) + { + return std::vector(); + } const vtkSMPropertyHelper inputHelper(proxy->GetProperty("Input")); - auto inputProxy = vtkSMSourceProxy::SafeDownCast(inputHelper.GetAsProxy()); + const auto inputProxy = vtkSMSourceProxy::SafeDownCast(inputHelper.GetAsProxy()); const int port = inputHelper.GetOutputPort(); if (!inputProxy || !inputProxy->GetOutputPort(port)) { // no input. - vtkWarningWithObjectMacro(proxy, "No input present. Cannot determine data ranges."); - return false; + vtkDebugWithObjectMacro(proxy, "No input present. Cannot determine data ranges."); + return std::vector(blockSelectors.size(), false); + } + + std::vector arrayInfos(blockSelectors.size(), nullptr); + for (size_t i = 0; i < blockSelectors.size(); ++i) + { + vtkPVTemporalDataInformation* blockDataInfo = + inputProxy->GetOutputPort(port)->GetTemporalSubsetDataInformation( + blockSelectors[i].c_str(), vtkSMPropertyHelper(proxy, "Assembly", true).GetAsString()); + vtkPVArrayInformation* blockArrayInfo = + blockDataInfo->GetArrayInformation(arrayName, attributeType); + arrayInfos[i] = blockArrayInfo; + } + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + proxy, blockSelectors, arrayInfos); +} + +//---------------------------------------------------------------------------- +std::vector +vtkSMColorMapEditorHelper::RescaleSelectedTransferFunctionToDataRangeOverTime(vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRangeOverTime(proxy) }; } +} - vtkPVTemporalDataInformation* blockDataInfo = - inputProxy->GetOutputPort(port)->GetTemporalSubsetDataInformation( - blockSelector.c_str(), vtkSMPropertyHelper(proxy, "Assembly", true).GetAsString()); - vtkPVArrayInformation* blockArrayInfo = - blockDataInfo->GetArrayInformation(arrayName, attributeType); - return blockArrayInfo ? vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange( - proxy, blockSelector, blockArrayInfo) - : false; +//---------------------------------------------------------------------------- +std::vector +vtkSMColorMapEditorHelper::RescaleSelectedTransferFunctionToDataRangeOverTime( + vtkSMProxy* proxy, const char* arrayName, int attributeType) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), arrayName, attributeType); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRangeOverTime( + proxy, arrayName, attributeType) }; + } } //---------------------------------------------------------------------------- @@ -374,13 +741,9 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( const int indexedLookup = vtkSMPropertyHelper(lut, "IndexedLookup").GetAsInt(); if (indexedLookup > 0) { - vtkPVProminentValuesInformation* prominentValues = - vtkSMColorMapEditorHelper::GetProminentValuesInformationForColorArray(proxy); - auto activeAnnotations = vtkSmartPointer::New(); - auto activeIndexedColors = vtkSmartPointer::New(); - vtkSmartPointer uniqueValues; - - uniqueValues.TakeReference(prominentValues->GetProminentComponentValues(component)); + vtkSmartPointer uniqueValues = vtk::TakeSmartPointer( + vtkSMColorMapEditorHelper::GetProminentValuesInformationForColorArray(proxy) + ->GetProminentComponentValues(component)); vtkSMStringVectorProperty* allAnnotations = vtkSMStringVectorProperty::SafeDownCast(lut->GetProperty("Annotations")); @@ -388,7 +751,7 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( vtkSMStringVectorProperty::SafeDownCast(lut->GetProperty("ActiveAnnotatedValues")); if (uniqueValues && allAnnotations && activeAnnotatedValuesProperty) { - auto activeAnnotatedValues = vtkSmartPointer::New(); + vtkNew activeAnnotatedValues; if (extend) { activeAnnotatedValuesProperty->GetElements(activeAnnotatedValues); @@ -418,7 +781,7 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( double rangeOpacity[2]; bool useOpacityArray = false; - if (auto uoaProperty = proxy->GetProperty("UseSeparateOpacityArray")) + if (vtkSMProperty* uoaProperty = proxy->GetProperty("UseSeparateOpacityArray")) { useOpacityArray = vtkSMPropertyHelper(uoaProperty).GetAsInt() == 1; } @@ -499,114 +862,140 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange(vtkSMProxy* proxy, - const std::string& blockSelector, vtkPVArrayInformation* info, bool extend, bool force) +std::vector vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + vtkSMProxy* proxy, const std::vector& blockSelectors, + std::vector infos, bool extend, bool force) { - if (!info) + if (blockSelectors.empty()) { - vtkGenericWarningMacro("Could not determine array range."); - return false; + return std::vector(); } - - vtkSMProxy* blockLut = vtkSMColorMapEditorHelper::GetBlockLookupTable(proxy, blockSelector); - if (!blockLut) + const std::vector blockUsingScalarColoring = + vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(proxy, blockSelectors); + // if no block is using scalar coloring + if (!std::any_of(blockUsingScalarColoring.begin(), blockUsingScalarColoring.end(), + [](vtkTypeBool b) { return b; })) { - return false; + // nothing to do. + return std::vector(blockSelectors.size(), false); } - if (force == false && - vtkSMPropertyHelper(blockLut, "AutomaticRescaleRangeMode", true).GetAsInt() == - vtkSMTransferFunctionManager::NEVER) - { - // nothing to change, range is locked. - return true; - } + std::vector blockLuts = + vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, blockSelectors); - // We need to determine the component number to use from the lut. - int component = -1; - if (blockLut && vtkSMPropertyHelper(blockLut, "VectorMode").GetAsInt() != 0) - { - component = vtkSMPropertyHelper(blockLut, "VectorComponent").GetAsInt(); - } + std::vector blockProminentValues = + vtkSMColorMapEditorHelper::GetBlocksProminentValuesInformationForColorArray( + proxy, blockSelectors); - if (blockLut && component < info->GetNumberOfComponents()) + std::vector results(blockSelectors.size(), false); + for (size_t i = 0; i < blockSelectors.size(); ++i) { - const int indexedLookup = vtkSMPropertyHelper(blockLut, "IndexedLookup").GetAsInt(); - if (indexedLookup) + vtkPVArrayInformation* info = infos[i]; + if (!info) { - auto prominentValues = - vtkSMColorMapEditorHelper::GetBlockProminentValuesInformationForColorArray( - proxy, blockSelector); - auto activeAnnotations = vtkSmartPointer::New(); - auto activeIndexedColors = vtkSmartPointer::New(); - vtkSmartPointer uniqueValues; + results[i] = false; + continue; + } - uniqueValues.TakeReference(prominentValues->GetProminentComponentValues(component)); + vtkSMProxy* blockLut = blockLuts[i]; + if (!blockLut) + { + results[i] = false; + continue; + } - auto allAnnotations = - vtkSMStringVectorProperty::SafeDownCast(blockLut->GetProperty("Annotations")); - auto activeAnnotatedValuesProperty = - vtkSMStringVectorProperty::SafeDownCast(blockLut->GetProperty("ActiveAnnotatedValues")); - if (uniqueValues && allAnnotations && activeAnnotatedValuesProperty) - { - auto activeAnnotatedValues = vtkSmartPointer::New(); + if (force == false && + vtkSMPropertyHelper(blockLut, "AutomaticRescaleRangeMode", true).GetAsInt() == + vtkSMTransferFunctionManager::NEVER) + { + // nothing to change, range is locked. + results[i] = true; + continue; + } - if (extend) - { - activeAnnotatedValuesProperty->GetElements(activeAnnotatedValues); - } + // We need to determine the component number to use from the lut. + int component = -1; + if (blockLut && vtkSMPropertyHelper(blockLut, "VectorMode").GetAsInt() != 0) + { + component = vtkSMPropertyHelper(blockLut, "VectorComponent").GetAsInt(); + } - for (int idx = 0; idx < uniqueValues->GetNumberOfTuples(); ++idx) + if (blockLut && component < info->GetNumberOfComponents()) + { + const int indexedLookup = vtkSMPropertyHelper(blockLut, "IndexedLookup").GetAsInt(); + if (indexedLookup > 0) + { + vtkSmartPointer uniqueValues = + vtk::TakeSmartPointer(blockProminentValues[i]->GetProminentComponentValues(component)); + + const auto allAnnotations = + vtkSMStringVectorProperty::SafeDownCast(blockLut->GetProperty("Annotations")); + const auto activeAnnotatedValuesProperty = + vtkSMStringVectorProperty::SafeDownCast(blockLut->GetProperty("ActiveAnnotatedValues")); + if (uniqueValues && allAnnotations && activeAnnotatedValuesProperty) { - // Look up index of color corresponding to the annotation - for (unsigned int j = 0; j < allAnnotations->GetNumberOfElements() / 2; ++j) + vtkNew activeAnnotatedValues; + if (extend) { - const vtkVariant annotatedValue(allAnnotations->GetElement(2 * j + 0)); - if (annotatedValue == uniqueValues->GetVariantValue(idx)) + activeAnnotatedValuesProperty->GetElements(activeAnnotatedValues); + } + + for (int idx = 0; idx < uniqueValues->GetNumberOfTuples(); ++idx) + { + // Look up index of color corresponding to the annotation + for (unsigned int j = 0; j < allAnnotations->GetNumberOfElements() / 2; ++j) { - activeAnnotatedValues->AddString(allAnnotations->GetElement(2 * j + 0)); - break; + const vtkVariant annotatedValue(allAnnotations->GetElement(2 * j + 0)); + if (annotatedValue == uniqueValues->GetVariantValue(idx)) + { + activeAnnotatedValues->AddString(allAnnotations->GetElement(2 * j + 0)); + break; + } } } + + activeAnnotatedValuesProperty->SetElements(activeAnnotatedValues); + blockLut->UpdateVTKObjects(); + } + } + else + { + double rangeColor[2]; + double rangeOpacity[2]; + + // No opacity array for block coloring + { + info->GetComponentFiniteRange(component, rangeColor); + rangeOpacity[0] = rangeColor[0]; + rangeOpacity[1] = rangeColor[1]; } - activeAnnotatedValuesProperty->SetElements(activeAnnotatedValues); - blockLut->UpdateVTKObjects(); + // the range must be large enough, compared to values order of magnitude + // If data range is too small then we tweak it a bit so scalar mapping + // produces valid/reproducible results. + vtkSMCoreUtilities::AdjustRange(rangeColor); + vtkSMCoreUtilities::AdjustRange(rangeOpacity); + + if (blockLut && rangeColor[1] >= rangeColor[0]) + { + vtkSMTransferFunctionProxy::RescaleTransferFunction(blockLut, rangeColor, extend); + vtkSMProxy* sof_lut = + vtkSMPropertyHelper(blockLut, "ScalarOpacityFunction", true).GetAsProxy(); + if (sof_lut && rangeOpacity[1] >= rangeOpacity[0]) + { + vtkSMTransferFunctionProxy::RescaleTransferFunction(sof_lut, rangeOpacity, extend); + } + } + results[i] = (blockLut != nullptr); } } else { - double rangeColor[2]; - double rangeOpacity[2]; - - // No opacity array for block coloring - { - info->GetComponentFiniteRange(component, rangeColor); - rangeOpacity[0] = rangeColor[0]; - rangeOpacity[1] = rangeColor[1]; - } - - // the range must be large enough, compared to values order of magnitude - // If data range is too small then we tweak it a bit so scalar mapping - // produces valid/reproducible results. - vtkSMCoreUtilities::AdjustRange(rangeColor); - vtkSMCoreUtilities::AdjustRange(rangeOpacity); - - if (blockLut && rangeColor[1] >= rangeColor[0]) - { - vtkSMTransferFunctionProxy::RescaleTransferFunction(blockLut, rangeColor, extend); - vtkSMProxy* sof_lut = - vtkSMPropertyHelper(blockLut, "ScalarOpacityFunction", true).GetAsProxy(); - if (sof_lut && rangeOpacity[1] >= rangeOpacity[0]) - { - vtkSMTransferFunctionProxy::RescaleTransferFunction(sof_lut, rangeOpacity, extend); - } - } - return blockLut; - } - } - return false; -} + results[i] = false; + } + } + return results; +} //---------------------------------------------------------------------------- bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToVisibleRange( @@ -635,7 +1024,7 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToVisibleRange( } const vtkSMPropertyHelper inputHelper(proxy->GetProperty("Input")); - auto inputProxy = vtkSMSourceProxy::SafeDownCast(inputHelper.GetAsProxy()); + const auto inputProxy = vtkSMSourceProxy::SafeDownCast(inputHelper.GetAsProxy()); const int port = inputHelper.GetOutputPort(); if (!inputProxy || !inputProxy->GetOutputPort(port)) { @@ -719,91 +1108,6 @@ bool vtkSMColorMapEditorHelper::RescaleTransferFunctionToVisibleRange( return true; } -//---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToVisibleRange( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) -{ - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector)) - { - // we are not using scalar coloring, nothing to do. - return false; - } - - const auto blockArrayAttributeTypeAndName = - vtkSMColorMapEditorHelper::GetBlockColorArray(proxy, blockSelector); - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToVisibleRange(proxy, view, - blockSelector, blockArrayAttributeTypeAndName.second.c_str(), - blockArrayAttributeTypeAndName.first); -} - -//---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToVisibleRange(vtkSMProxy* proxy, - vtkSMProxy* view, const std::string& blockSelector, const char* arrayName, int attributeType) -{ - auto rview = vtkSMRenderViewProxy::SafeDownCast(view); - if (!rview || !arrayName || arrayName[0] == 0) - { - return false; - } - - const vtkSMPropertyHelper helper(proxy->GetProperty("Input")); - vtkSMSourceProxy* inputProxy = vtkSMSourceProxy::SafeDownCast(helper.GetAsProxy()); - const int port = helper.GetOutputPort(); - if (!inputProxy || !inputProxy->GetOutputPort(port)) - { - vtkGenericWarningMacro("No input present. Cannot determine data ranges."); - return false; - } - - auto blockDataInfo = inputProxy->GetOutputPort(port)->GetSubsetDataInformation( - blockSelector.c_str(), vtkSMPropertyHelper(proxy->GetProperty("Assembly"), true).GetAsString()); - vtkPVArrayInformation* info = blockDataInfo->GetArrayInformation(arrayName, attributeType); - if (!info) - { - return false; - } - - const bool hasBlockLUT = - proxy->GetProperty("BlockLookupTableSelectors") && proxy->GetProperty("BlockLookupTables"); - if (!hasBlockLUT) - { - // No LookupTable found. - return false; - } - - vtkSMProxy* blockLUT = vtkSMColorMapEditorHelper::GetBlockLookupTable(proxy, blockSelector); - - // We need to determine the component number to use from the lut. - int component = -1; - if (blockLUT && vtkSMPropertyHelper(blockLUT, "VectorMode").GetAsInt() != 0) - { - component = vtkSMPropertyHelper(blockLUT, "VectorComponent").GetAsInt(); - } - if (component >= info->GetNumberOfComponents()) - { - // something amiss, the component request is not present in the dataset. - // give up. - return false; - } - - double range[2]; - if (!rview->ComputeVisibleScalarRange(attributeType, arrayName, component, range)) - { - return false; - } - - if (blockLUT) - { - vtkSMTransferFunctionProxy::RescaleTransferFunction(blockLUT, range, false); - vtkSMProxy* sof_lut = vtkSMPropertyHelper(blockLUT, "ScalarOpacityFunction", true).GetAsProxy(); - if (sof_lut) - { - vtkSMTransferFunctionProxy::RescaleTransferFunction(sof_lut, range, false); - } - } - return true; -} - //---------------------------------------------------------------------------- bool vtkSMColorMapEditorHelper::SetScalarColoring( vtkSMProxy* proxy, const char* arrayName, int attributeType) @@ -848,7 +1152,8 @@ bool vtkSMColorMapEditorHelper::SetScalarColoringInternal( } auto* arraySettings = vtkPVRepresentedArrayListSettings::GetInstance(); - auto* arrayInfo = vtkSMColorMapEditorHelper::GetArrayInformationForColorArray(proxy, false); + vtkPVArrayInformation* arrayInfo = + vtkSMColorMapEditorHelper::GetArrayInformationForColorArray(proxy, false); const bool forceComponentMode = (arrayInfo && arraySettings && !arraySettings->ShouldUseMagnitudeMode(arrayInfo->GetNumberOfComponents())); if (forceComponentMode && (!useComponent || component < 0)) @@ -863,34 +1168,34 @@ bool vtkSMColorMapEditorHelper::SetScalarColoringInternal( const std::string decoratedArrayName = vtkSMColorMapEditorHelper::GetDecoratedArrayName(proxy, arrayName); vtkNew mgr; - vtkSMProxy* lutProxy = nullptr; + vtkSMProxy* lut = nullptr; if (vtkSMProperty* lutProperty = proxy->GetProperty("LookupTable")) { - lutProxy = + lut = mgr->GetColorTransferFunction(decoratedArrayName.c_str(), proxy->GetSessionProxyManager()); if (useComponent || forceComponentMode) { if (component >= 0) { - vtkSMPropertyHelper(lutProxy, "VectorMode").Set("Component"); - vtkSMPropertyHelper(lutProxy, "VectorComponent").Set(component); - lutProxy->UpdateVTKObjects(); + vtkSMPropertyHelper(lut, "VectorMode").Set("Component"); + vtkSMPropertyHelper(lut, "VectorComponent").Set(component); + lut->UpdateVTKObjects(); } else { - vtkSMPropertyHelper(lutProxy, "VectorMode").Set("Magnitude"); - lutProxy->UpdateVTKObjects(); + vtkSMPropertyHelper(lut, "VectorMode").Set("Magnitude"); + lut->UpdateVTKObjects(); } } else { // No Component defined for coloring, in order to generate a valid trace // a component is needed, recover currently used component - const char* vectorMode = vtkSMPropertyHelper(lutProxy, "VectorMode").GetAsString(); + const char* vectorMode = vtkSMPropertyHelper(lut, "VectorMode").GetAsString(); haveComponent = true; if (strcmp(vectorMode, "Component") == 0) { - component = vtkSMPropertyHelper(lutProxy, "VectorComponent").GetAsInt(); + component = vtkSMPropertyHelper(lut, "VectorComponent").GetAsInt(); } else // Magnitude { @@ -898,7 +1203,7 @@ bool vtkSMColorMapEditorHelper::SetScalarColoringInternal( } } - vtkSMPropertyHelper(lutProperty).Set(lutProxy); + vtkSMPropertyHelper(lutProperty).Set(lut); // Get the array information for the color array to determine transfer function properties vtkPVArrayInformation* colorArrayInfo = @@ -907,8 +1212,8 @@ bool vtkSMColorMapEditorHelper::SetScalarColoringInternal( { if (colorArrayInfo->GetDataType() == VTK_STRING) { - vtkSMPropertyHelper(lutProxy, "IndexedLookup", true).Set(1); - lutProxy->UpdateVTKObjects(); + vtkSMPropertyHelper(lut, "IndexedLookup", true).Set(1); + lut->UpdateVTKObjects(); } if (haveComponent) { @@ -952,10 +1257,10 @@ bool vtkSMColorMapEditorHelper::SetScalarColoringInternal( mgr->GetTransferFunction2D(decoratedArrayName.c_str(), proxy->GetSessionProxyManager()); vtkSMPropertyHelper(tf2dProperty).Set(tf2dProxy); proxy->UpdateProperty("TransferFunction2D"); - if (lutProxy && useTransfer2D) + if (lut && useTransfer2D) { - vtkSMPropertyHelper(lutProxy, "Using2DTransferFunction").Set(useTransfer2D); - lutProxy->UpdateVTKObjects(); + vtkSMPropertyHelper(lut, "Using2DTransferFunction").Set(useTransfer2D); + lut->UpdateVTKObjects(); } } @@ -964,160 +1269,246 @@ bool vtkSMColorMapEditorHelper::SetScalarColoringInternal( } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::SetBlockScalarColoring( - vtkSMProxy* proxy, const std::string& blockSelector, const char* arrayName, int attributeType) +std::vector vtkSMColorMapEditorHelper::SetBlocksScalarColoring(vtkSMProxy* proxy, + const std::vector& blockSelectors, const char* arrayName, int attributeType) { - return vtkSMColorMapEditorHelper::SetBlockScalarColoringInternal( - proxy, blockSelector, arrayName, attributeType, false, -1); + if (blockSelectors.empty()) + { + return std::vector(); + } + return vtkSMColorMapEditorHelper::SetBlocksScalarColoringInternal( + proxy, blockSelectors, arrayName, attributeType, false, -1); } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::SetBlockScalarColoring(vtkSMProxy* proxy, - const std::string& blockSelector, const char* arrayName, int attributeType, int component) +std::vector vtkSMColorMapEditorHelper::SetBlocksScalarColoring(vtkSMProxy* proxy, + const std::vector& blockSelectors, const char* arrayName, int attributeType, + int component) { - return vtkSMColorMapEditorHelper::SetBlockScalarColoringInternal( - proxy, blockSelector, arrayName, attributeType, true, component); + if (blockSelectors.empty()) + { + return std::vector(); + } + return vtkSMColorMapEditorHelper::SetBlocksScalarColoringInternal( + proxy, blockSelectors, arrayName, attributeType, true, component); } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::SetBlockScalarColoringInternal(vtkSMProxy* proxy, - const std::string& blockSelector, const char* arrayName, int attributeType, bool useComponent, - int component) +std::vector vtkSMColorMapEditorHelper::SetSelectedScalarColoring( + vtkSMProxy* proxy, const char* arrayName, int attributeType) { - auto repr = vtkSMPVRepresentationProxy::SafeDownCast(proxy); + return vtkSMColorMapEditorHelper::SetSelectedScalarColoringInternal( + proxy, arrayName, attributeType, false, -1); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::SetSelectedScalarColoring( + vtkSMProxy* proxy, const char* arrayName, int attributeType, int component) +{ + return vtkSMColorMapEditorHelper::SetSelectedScalarColoringInternal( + proxy, arrayName, attributeType, true, component); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::SetBlocksScalarColoringInternal( + vtkSMProxy* proxy, const std::vector& blockSelectors, const char* arrayName, + int attributeType, bool useComponent, int component) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto repr = vtkSMPVRepresentationProxy::SafeDownCast(proxy); if (!repr) { - return false; + return std::vector(blockSelectors.size(), false); } + const std::vector blockUsingScalarColorings = + vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(proxy, blockSelectors); - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector) && + // if all are not using scalar coloring + if (std::all_of(blockUsingScalarColorings.begin(), blockUsingScalarColorings.end(), + [](vtkTypeBool b) { return !b; }) && (arrayName == nullptr || arrayName[0] == 0)) { // scalar coloring already off. Nothing to do. - return true; - } - auto blockColorArray = repr->GetProperty("BlockColorArrayNames"); - if (!blockColorArray) - { - vtkWarningWithObjectMacro(repr, "No 'BlockColorArrayNames' property found."); - return false; + return std::vector(blockSelectors.size(), true); } - vtkSMColorMapEditorHelper::SetBlockColorArray( - repr, blockSelector, attributeType, arrayName ? arrayName : ""); - - if (arrayName == nullptr || arrayName[0] == '\0') + // if any block is using scalar coloring and no array name provided + if (arrayName == nullptr || arrayName[0] == 0) { - SM_SCOPED_TRACE(SetBlockScalarColoring) + SM_SCOPED_TRACE(SetBlocksScalarColoring) .arg("display", repr) - .arg("block_selector", blockSelector.c_str()) + .arg("block_selectors", blockSelectors) .arg("arrayname", arrayName) .arg("attribute_type", attributeType); - vtkSMColorMapEditorHelper::RemoveBlockLookupTable(repr, blockSelector); + // IMPORTANT!!! First remove luts and then color arrays + vtkSMColorMapEditorHelper::RemoveBlocksLookupTables(repr, blockSelectors); + vtkSMColorMapEditorHelper::RemoveBlocksColorArrays(repr, blockSelectors); // Scalar Opacity is not supported per block yet repr->UpdateVTKObjects(); - return true; + return std::vector(blockSelectors.size(), true); + } + else + { + vtkSMColorMapEditorHelper::SetBlocksColorArray(repr, blockSelectors, attributeType, arrayName); } auto* arraySettings = vtkPVRepresentedArrayListSettings::GetInstance(); - auto blockArrayInfo = - vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray(repr, blockSelector, false); - const bool forceComponentMode = (blockArrayInfo && arraySettings && - !arraySettings->ShouldUseMagnitudeMode(blockArrayInfo->GetNumberOfComponents())); + const std::vector blockArrayInfos = + vtkSMColorMapEditorHelper::GetBlocksArrayInformationForColorArray(repr, blockSelectors); + // if there is no block's array information that is available + if (std::all_of(blockArrayInfos.begin(), blockArrayInfos.end(), + [](vtkPVArrayInformation* info) { return info == nullptr; })) + { + vtkWarningWithObjectMacro(repr, "Could not determine array information."); + return std::vector(blockSelectors.size(), false); + } + // get first valid block's array information + int firstValidInfoIndex = -1; + for (size_t i = 0; i < blockArrayInfos.size(); ++i) + { + if (blockArrayInfos[i]) + { + firstValidInfoIndex = static_cast(i); + break; + } + } + + // since all blocks should use the same array, infos should have the same number of components + const bool forceComponentMode = (blockArrayInfos[firstValidInfoIndex] && arraySettings && + !arraySettings->ShouldUseMagnitudeMode( + blockArrayInfos[firstValidInfoIndex]->GetNumberOfComponents())); if (forceComponentMode && (!useComponent || component < 0)) { component = 0; } - // Now, setup transfer functions. - bool haveComponent = useComponent; - const bool separate = vtkSMColorMapEditorHelper::GetBlockUseSeparateColorMap(repr, blockSelector); - const std::string decoratedArrayName = - vtkSMColorMapEditorHelper::GetBlockDecoratedArrayName(repr, blockSelector, arrayName); - const bool hasBlockLUTProperty = repr->GetProperty("BlockLookupTableSelectors") != nullptr && - repr->GetProperty("BlockLookupTables") != nullptr; + const bool hasBlockLUTProperty = repr->GetProperty("BlockLookupTables") != nullptr; + if (!hasBlockLUTProperty) + { + vtkDebugWithObjectMacro(repr, "No 'BlockLookupTables' found."); + return std::vector(blockSelectors.size(), false); + } + + const std::vector separates = + vtkSMColorMapEditorHelper::GetBlocksUseSeparateColorMaps(repr, blockSelectors); + // if all blocks are not using separate color maps + const bool sameLut = + std::all_of(separates.begin(), separates.end(), [](int s) { return s != 1; }); + + const std::vector decoratedArrayNames = + vtkSMColorMapEditorHelper::GetBlocksDecoratedArrayNames(repr, blockSelectors, arrayName); + vtkNew mgr; - vtkSMProxy* lutProxy = nullptr; - if (hasBlockLUTProperty) + bool haveComponent = useComponent || forceComponentMode ? useComponent : true; + for (size_t i = 0; i < blockSelectors.size(); ++i) { - lutProxy = - mgr->GetColorTransferFunction(decoratedArrayName.c_str(), repr->GetSessionProxyManager()); + vtkSMProxy* blockLut = + mgr->GetColorTransferFunction(decoratedArrayNames[i].c_str(), repr->GetSessionProxyManager()); if (useComponent || forceComponentMode) { if (component >= 0) { - vtkSMPropertyHelper(lutProxy, "VectorMode").Set("Component"); - vtkSMPropertyHelper(lutProxy, "VectorComponent").Set(component); - lutProxy->UpdateVTKObjects(); + vtkSMPropertyHelper(blockLut, "VectorMode").Set("Component"); + vtkSMPropertyHelper(blockLut, "VectorComponent").Set(component); + blockLut->UpdateVTKObjects(); } else { - vtkSMPropertyHelper(lutProxy, "VectorMode").Set("Magnitude"); - lutProxy->UpdateVTKObjects(); + vtkSMPropertyHelper(blockLut, "VectorMode").Set("Magnitude"); + blockLut->UpdateVTKObjects(); } } else { // No Component defined for coloring, in order to generate a valid trace // a component is needed, recover currently used component - const char* vectorMode = vtkSMPropertyHelper(lutProxy, "VectorMode").GetAsString(); - haveComponent = true; + const char* vectorMode = vtkSMPropertyHelper(blockLut, "VectorMode").GetAsString(); if (strcmp(vectorMode, "Component") == 0) { - component = vtkSMPropertyHelper(lutProxy, "VectorComponent").GetAsInt(); + // Many blocks can override the component because it should be the same for all. + component = vtkSMPropertyHelper(blockLut, "VectorComponent").GetAsInt(); } else // Magnitude { + // Many blocks can override the component because it should be the same for all. component = -1; } } + // Stop quickly if all blocks will be using the same lut + if (sameLut) + { + vtkSMColorMapEditorHelper::SetBlocksLookupTable(repr, blockSelectors, blockLut); + break; + } + else + { + vtkSMColorMapEditorHelper::SetBlockLookupTable(repr, blockSelectors[i], blockLut); + } + } - vtkSMColorMapEditorHelper::SetBlockLookupTable(repr, blockSelector, lutProxy); + // get component name + std::string componentName; + if (haveComponent) + { + // the component name is the same for all blocks + componentName = blockArrayInfos[firstValidInfoIndex]->GetComponentName(component); + haveComponent = !componentName.empty(); + } - // Get the array information for the color array to determine transfer function properties - auto blockColorArrayInfo = - vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray(repr, blockSelector, true); - if (blockColorArrayInfo) + for (size_t i = 0; i < blockSelectors.size(); ++i) + { + vtkSMProxy* blockLut = + mgr->GetColorTransferFunction(decoratedArrayNames[i].c_str(), repr->GetSessionProxyManager()); + if (blockLut && blockArrayInfos[i] && blockArrayInfos[i]->GetDataType() == VTK_STRING) { - if (blockColorArrayInfo->GetDataType() == VTK_STRING) - { - vtkSMPropertyHelper(lutProxy, "IndexedLookup", true).Set(1); - lutProxy->UpdateVTKObjects(); - } - if (haveComponent) - { - const char* componentName = blockColorArrayInfo->GetComponentName(component); - if (strcmp(componentName, "") != 0) - { - SM_SCOPED_TRACE(SetBlockScalarColoring) - .arg("display", repr) - .arg("block_selector", blockSelector.c_str()) - .arg("arrayname", arrayName) - .arg("attribute_type", attributeType) - .arg("component", componentName) - .arg("separate", separate); - } - else - { - haveComponent = false; - } - } + vtkSMPropertyHelper(blockLut, "IndexedLookup", true).Set(1); + blockLut->UpdateVTKObjects(); } } - if (!haveComponent) + if (haveComponent) { - SM_SCOPED_TRACE(SetBlockScalarColoring) + SM_SCOPED_TRACE(SetBlocksScalarColoring) .arg("display", repr) - .arg("block_selector", blockSelector.c_str()) + .arg("block_selectors", blockSelectors) .arg("arrayname", arrayName) .arg("attribute_type", attributeType) - .arg("separate", separate); + .arg("component", componentName.c_str()) + .arg("separate", !sameLut); + } + else + { + SM_SCOPED_TRACE(SetBlocksScalarColoring) + .arg("display", repr) + .arg("block_selectors", blockSelectors) + .arg("arrayname", arrayName) + .arg("attribute_type", attributeType) + .arg("separate", !sameLut); } - repr->UpdateVTKObjects(); - return true; + + return std::vector(blockSelectors.size(), true); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::SetSelectedScalarColoringInternal( + vtkSMProxy* proxy, const char* arrayName, int attributeType, bool useComponent, int component) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::SetBlocksScalarColoringInternal(proxy, + vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), arrayName, attributeType, + useComponent, component); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::SetScalarColoringInternal( + proxy, arrayName, attributeType, useComponent, component) }; + } } //---------------------------------------------------------------------------- @@ -1156,262 +1547,1407 @@ std::string vtkSMColorMapEditorHelper::GetDecoratedArrayName( } //---------------------------------------------------------------------------- -std::string vtkSMColorMapEditorHelper::GetBlockDecoratedArrayName( - vtkSMProxy* proxy, const std::string& blockSelector, const std::string& arrayName) +std::vector vtkSMColorMapEditorHelper::GetBlocksDecoratedArrayNames( + vtkSMProxy* proxy, const std::vector& blockSelectors, const std::string& arrayName) { - std::ostringstream ss; - ss << arrayName; - if (vtkSMColorMapEditorHelper::GetBlockUseSeparateColorMap(proxy, blockSelector)) - { - std::ostringstream ss1; - ss1 << "Separate_" << proxy->GetGlobalIDAsString() << "_" << blockSelector << "_" << ss.str(); - return ss1.str(); - } - if (vtkSMPropertyHelper(proxy, "UseSeparateColorMap", true).GetAsInt()) + if (blockSelectors.empty()) { - // Use global id for separate color map - std::ostringstream ss1; - ss1 << "Separate_" << proxy->GetGlobalIDAsString() << "_" << ss.str(); - return ss1.str(); + return std::vector(); } - return ss.str(); -} + std::vector blocksUseSeparateColorMap = + vtkSMColorMapEditorHelper::GetBlocksUseSeparateColorMaps(proxy, blockSelectors); + const int representationSeparateColorMap = + vtkSMPropertyHelper(proxy, "UseSeparateColorMap", true).GetAsInt(); -//---------------------------------------------------------------------------- -void vtkSMColorMapEditorHelper::SetBlockColorArray( - vtkSMProxy* proxy, const std::string& blockSelector, int attributeType, std::string arrayName) -{ - auto blockColorArray = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); - if (!blockColorArray) - { - vtkWarningWithObjectMacro(proxy, "No 'BlockColorArrayNames' property found."); - return; - } - assert(blockColorArray->GetNumberOfElementsPerCommand() == 3); - if (arrayName.empty()) + std::ostringstream ss; + ss << arrayName; + std::vector decoratedArrayNames(blockSelectors.size()); + for (size_t i = 0; i < blockSelectors.size(); ++i) { - // find the block selector - bool found = false; - unsigned selectorIdx = 0; - for (unsigned int i = 0; i < blockColorArray->GetNumberOfElements(); i += 3) - { - if (blockColorArray->GetElement(i) == blockSelector) - { - // found the block selector - selectorIdx = i; - found = true; - break; - } - } - if (found) + if (blocksUseSeparateColorMap[i] == 1) { - std::vector blockColorArrayVec; - // remove the block selector and color array - for (unsigned int i = 0; i < blockColorArray->GetNumberOfElements(); i += 3) - { - if (i != selectorIdx) - { - blockColorArrayVec.push_back(blockColorArray->GetElement(i)); - blockColorArrayVec.push_back(blockColorArray->GetElement(i + 1)); - blockColorArrayVec.push_back(blockColorArray->GetElement(i + 2)); - } - } - blockColorArray->SetElements(blockColorArrayVec); + std::ostringstream ss1; + ss1 << "Separate_" << proxy->GetGlobalIDAsString() << "_" << blockSelectors[i] << "_" + << ss.str(); + decoratedArrayNames[i] = ss1.str(); } - } - else - { - bool found = false; - for (unsigned int i = 0; i < blockColorArray->GetNumberOfElements(); i += 3) + else if (representationSeparateColorMap == 1) { - if (blockColorArray->GetElement(i) == blockSelector) - { - // found the block selector, update the color array - blockColorArray->SetElement(i + 1, std::to_string(attributeType).c_str()); - blockColorArray->SetElement(i + 2, arrayName.c_str()); - found = true; - break; - } + std::ostringstream ss1; + ss1 << "Separate_" << proxy->GetGlobalIDAsString() << "_" << blockSelectors[i] << "_" + << ss.str(); + decoratedArrayNames[i] = ss1.str(); } - if (!found) + else { - // add the block selector and color array - blockColorArray->AppendElements({ blockSelector, std::to_string(attributeType), arrayName }); + decoratedArrayNames[i] = ss.str(); } } + return decoratedArrayNames; } //---------------------------------------------------------------------------- -std::pair vtkSMColorMapEditorHelper::GetBlockColorArray( - vtkSMProxy* proxy, const std::string& blockSelector) +bool vtkSMColorMapEditorHelper::IsColorValid(Color color) { - auto blockColorArray = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); - if (!blockColorArray) + return std::all_of(color.begin(), color.end(), [](double c) { return c >= 0.0 && c <= 1.0; }); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetColor(vtkSMProxy* proxy, Color color) +{ + if (!vtkSMColorMapEditorHelper::IsColorValid(color)) { - vtkWarningWithObjectMacro(proxy, "No 'BlockColorArrayNames' property found."); - return std::make_pair(-1, std::string("")); + vtkWarningWithObjectMacro(proxy, "Invalid color."); + return; } - assert(blockColorArray->GetNumberOfElementsPerCommand() == 3); - for (unsigned int i = 0; i < blockColorArray->GetNumberOfElements(); i += 3) + vtkSMProperty* diffuse = proxy->GetProperty("DiffuseColor"); + vtkSMProperty* ambient = proxy->GetProperty("AmbientColor"); + if (diffuse == nullptr && ambient == nullptr) { - if (blockColorArray->GetElement(i) == blockSelector) - { - return std::make_pair(std::stoi(blockColorArray->GetElement(i + 1)), - std::string(blockColorArray->GetElement(i + 2))); - } + diffuse = proxy->GetProperty("Color"); } - return std::make_pair(-1, std::string("")); + SM_SCOPED_TRACE(PropertiesModified).arg("proxy", proxy).arg("comment", " change solid color"); + vtkSMPropertyHelper(diffuse, /*quiet=*/true).Set(color.data(), 3); + vtkSMPropertyHelper(ambient, /*quiet=*/true).Set(color.data(), 3); + proxy->UpdateVTKObjects(); } //---------------------------------------------------------------------------- -void vtkSMColorMapEditorHelper::SetBlockUseSeparateColorMap( - vtkSMProxy* proxy, const std::string& blockSelector, bool use) +void vtkSMColorMapEditorHelper::SetBlocksColor( + vtkSMProxy* proxy, const std::vector& blockSelectors, Color color) { - auto blockUseSeparateColorMaps = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockUseSeparateColorMaps")); - if (!blockUseSeparateColorMaps) + if (blockSelectors.empty()) { - vtkWarningWithObjectMacro(proxy, "No 'blockUseSeparateColorMaps' property found."); return; } - assert(blockUseSeparateColorMaps->GetNumberOfElementsPerCommand() == 2); - bool found = false; - for (unsigned int i = 0; i < blockUseSeparateColorMaps->GetNumberOfElements(); i += 3) + if (!vtkSMColorMapEditorHelper::IsColorValid(color)) { - if (blockUseSeparateColorMaps->GetElement(i) == blockSelector) - { - // found the block selector, update the color array - blockUseSeparateColorMaps->SetElement(i + 1, std::to_string(use).c_str()); - found = true; - break; - } + vtkWarningWithObjectMacro(proxy, "Invalid color."); + return; } - if (!found) + auto blockColorsProp = vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColors")); + if (!blockColorsProp) { - // add the block selector and color array - blockUseSeparateColorMaps->AppendElements({ blockSelector, std::to_string(use) }); + vtkDebugWithObjectMacro(proxy, "No 'BlockColors' property found."); + return; } -} - -//---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::GetBlockUseSeparateColorMap( - vtkSMProxy* proxy, const std::string& blockSelector) -{ - auto blockUseSeparateColorMaps = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockUseSeparateColorMaps")); - if (!blockUseSeparateColorMaps) + assert(blockColorsProp->GetNumberOfElementsPerCommand() == 4); + std::map blockColorsMap; + for (unsigned int i = 0; i < blockColorsProp->GetNumberOfElements(); i += 4) { - vtkWarningWithObjectMacro(proxy, "No 'blockUseSeparateColorMaps' property found."); - return false; + blockColorsMap[blockColorsProp->GetElement(i)] = { + std::stod(blockColorsProp->GetElement(i + 1)), std::stod(blockColorsProp->GetElement(i + 2)), + std::stod(blockColorsProp->GetElement(i + 3)) + }; } - assert(blockUseSeparateColorMaps->GetNumberOfElementsPerCommand() == 2); - for (unsigned int i = 0; i < blockUseSeparateColorMaps->GetNumberOfElements(); i += 2) + for (const std::string& blockSelector : blockSelectors) { - if (blockUseSeparateColorMaps->GetElement(i) == blockSelector) - { - if (std::stoi(blockUseSeparateColorMaps->GetElement(i + 1)) == 1) - { - return true; - } - } + blockColorsMap[blockSelector] = color; } - return false; + std::vector blockColorsVec; + blockColorsVec.reserve(blockColorsMap.size() * 4); + for (const auto& blockColor : blockColorsMap) + { + blockColorsVec.push_back(blockColor.first); + blockColorsVec.push_back(std::to_string(blockColor.second[0])); + blockColorsVec.push_back(std::to_string(blockColor.second[1])); + blockColorsVec.push_back(std::to_string(blockColor.second[2])); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) solid Color"); + blockColorsProp->SetElements(blockColorsVec); + proxy->UpdateProperty("BlockColors"); } //---------------------------------------------------------------------------- -void vtkSMColorMapEditorHelper::SetBlockLookupTable( - vtkSMProxy* proxy, const std::string& blockSelector, vtkSMProxy* lutProxy) +void vtkSMColorMapEditorHelper::RemoveBlocksColors( + vtkSMProxy* proxy, const std::vector& blockSelectors) { - auto blockLUTSelectors = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockLookupTableSelectors")); + if (blockSelectors.empty()) + { + return; + } + auto blockColorsProp = vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColors")); + if (!blockColorsProp) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockColors' property found."); + return; + } + assert(blockColorsProp->GetNumberOfElementsPerCommand() == 4); + std::map blockColorsMap; + for (unsigned int i = 0; i < blockColorsProp->GetNumberOfElements(); i += 4) + { + blockColorsMap[blockColorsProp->GetElement(i)] = { + std::stod(blockColorsProp->GetElement(i + 1)), std::stod(blockColorsProp->GetElement(i + 2)), + std::stod(blockColorsProp->GetElement(i + 3)) + }; + } + for (const std::string& blockSelector : blockSelectors) + { + blockColorsMap.erase(blockSelector); + } + std::vector blockColorsVec; + blockColorsVec.reserve(blockColorsMap.size() * 4); + for (const auto& blockColor : blockColorsMap) + { + blockColorsVec.push_back(blockColor.first); + blockColorsVec.push_back(std::to_string(blockColor.second[0])); + blockColorsVec.push_back(std::to_string(blockColor.second[1])); + blockColorsVec.push_back(std::to_string(blockColor.second[2])); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) solid Color"); + blockColorsProp->SetElements(blockColorsVec); + proxy->UpdateProperty("BlockColors"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetSelectedColor(vtkSMProxy* proxy, Color color) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + vtkSMColorMapEditorHelper::SetBlocksColor( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), color); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + vtkSMColorMapEditorHelper::SetColor(proxy, color); + } +} + +//---------------------------------------------------------------------------- +vtkSMColorMapEditorHelper::Color vtkSMColorMapEditorHelper::GetColor(vtkSMProxy* proxy) +{ + vtkSMProperty* diffuse = proxy->GetProperty("DiffuseColor"); + vtkSMProperty* ambient = proxy->GetProperty("AmbientColor"); + if (diffuse == nullptr && ambient == nullptr) + { + diffuse = proxy->GetProperty("Color"); + } + Color color; + if (diffuse || ambient) + { + vtkSMPropertyHelper(diffuse ? diffuse : ambient).Get(color.data(), 3); + } + else + { + color[0] = color[1] = color[2] = VTK_DOUBLE_MAX; + } + return color; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksColors( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto blockColorsProp = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColors")); + if (!blockColorsProp) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockColors' property found."); + return std::vector{ blockSelectors.size(), + { VTK_DOUBLE_MAX, VTK_DOUBLE_MAX, VTK_DOUBLE_MAX } }; + } + std::vector colors( + blockSelectors.size(), { VTK_DOUBLE_MAX, VTK_DOUBLE_MAX, VTK_DOUBLE_MAX }); + for (unsigned int i = 0; i < blockColorsProp->GetNumberOfElements(); i += 4) + { + for (unsigned int j = 0; j < blockSelectors.size(); ++j) + { + if (blockColorsProp->GetElement(i) == blockSelectors[j]) + { + colors[j] = { std::stod(blockColorsProp->GetElement(i + 1)), + std::stod(blockColorsProp->GetElement(i + 2)), + std::stod(blockColorsProp->GetElement(i + 3)) }; + break; + } + } + } + return colors; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedColors( + vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::GetBlocksColors( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::GetColor(proxy) }; + } +} + +//---------------------------------------------------------------------------- +vtkSMProperty* vtkSMColorMapEditorHelper::GetColorArrayProperty(vtkSMProxy* proxy) +{ + return proxy->GetProperty("ColorArrayName"); +} + +//---------------------------------------------------------------------------- +vtkSMProperty* vtkSMColorMapEditorHelper::GetBlockColorArrayProperty(vtkSMProxy* proxy) +{ + return proxy->GetProperty("BlockColorArrayNames"); +} + +//---------------------------------------------------------------------------- +vtkSMProperty* vtkSMColorMapEditorHelper::GetSelectedColorArrayProperty(vtkSMProxy* proxy) +{ + return this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks + ? vtkSMColorMapEditorHelper::GetBlockColorArrayProperty(proxy) + : vtkSMColorMapEditorHelper::GetColorArrayProperty(proxy); +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::IsColorArrayValid(const ColorArray& array) +{ + return array.first >= vtkDataObject::POINT && + array.first < vtkDataObject::NUMBER_OF_ATTRIBUTE_TYPES && !array.second.empty(); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetColorArray( + vtkSMProxy* proxy, int attributeType, std::string arrayName) +{ + if (!vtkSMColorMapEditorHelper::IsColorArrayValid(std::make_pair(attributeType, arrayName))) + { + vtkWarningWithObjectMacro(proxy, "Invalid color array."); + return; + } + SM_SCOPED_TRACE(PropertiesModified).arg("proxy", proxy).arg("comment", " change color array"); + vtkSMPropertyHelper(proxy, "ColorArrayName") + .SetInputArrayToProcess(attributeType, arrayName.c_str()); + proxy->UpdateProperty("ColorArrayName"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetBlocksColorArray(vtkSMProxy* proxy, + const std::vector& blockSelectors, int attributeType, std::string arrayName) +{ + if (blockSelectors.empty()) + { + return; + } + if (!vtkSMColorMapEditorHelper::IsColorArrayValid(std::make_pair(attributeType, arrayName))) + { + vtkWarningWithObjectMacro(proxy, "Invalid color array."); + return; + } + auto blockColorArrayNames = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); auto blockLUTs = vtkSMProxyProperty::SafeDownCast(proxy->GetProperty("BlockLookupTables")); - if (!blockLUTSelectors || !blockLUTs) + if (!blockColorArrayNames || !blockLUTs) + { + vtkDebugWithObjectMacro( + proxy, "No 'BlockColorArrayNames' or 'BlockLookupTables' properties found."); + return; + } + assert(blockColorArrayNames->GetNumberOfElementsPerCommand() == 3); + if (arrayName.empty()) + { + return; + } + std::map blockColorArrayMap; + std::map blockLUTsMap; + for (unsigned int i = 0; i < blockLUTs->GetNumberOfProxies(); ++i) + { + blockColorArrayMap[blockColorArrayNames->GetElement(3 * i)] = + std::make_pair(std::stoi(blockColorArrayNames->GetElement(3 * i + 1)), + std::string(blockColorArrayNames->GetElement(3 * i + 2))); + blockLUTsMap[blockColorArrayNames->GetElement(3 * i)] = blockLUTs->GetProxy(i); + } + for (const std::string& blockSelector : blockSelectors) + { + blockColorArrayMap[blockSelector] = std::make_pair(attributeType, arrayName); + blockLUTsMap[blockSelector] = nullptr; + } + std::vector blockColorArrayVec; + blockColorArrayVec.reserve(blockColorArrayMap.size() * 3); + std::vector blockLUTsVec; + blockLUTsVec.reserve(blockLUTsMap.size()); + for (const auto& colorArray : blockColorArrayMap) + { + blockColorArrayVec.push_back(colorArray.first); + blockColorArrayVec.push_back(std::to_string(colorArray.second.first)); + blockColorArrayVec.push_back(colorArray.second.second); + blockLUTsVec.push_back(blockLUTsMap[colorArray.first]); + } + // the following trace is not needed because this function is not supposed to be publicly called + // SM_SCOPED_TRACE(PropertiesModified) + // .arg("proxy", proxy) + // .arg("comment", " change block(s) color array"); + blockColorArrayNames->SetElements(blockColorArrayVec); + proxy->UpdateProperty("BlockColorArrayNames"); + blockLUTs->SetProxies(static_cast(blockLUTsVec.size()), blockLUTsVec.data()); + // no need to call UpdateProperty for BlockLookupTables since SetBlocksLookupTable will do that +} +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::RemoveBlocksColorArrays( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockColorArrayNames = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); + if (!blockColorArrayNames) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockColorArrayNames' property found."); + return; + } + assert(blockColorArrayNames->GetNumberOfElementsPerCommand() == 3); + std::map blockColorArrayMap; + for (unsigned int i = 0; i < blockColorArrayNames->GetNumberOfElements(); i += 3) + { + blockColorArrayMap[blockColorArrayNames->GetElement(i)] = + std::make_pair(std::stoi(blockColorArrayNames->GetElement(i + 1)), + std::string(blockColorArrayNames->GetElement(i + 2))); + } + for (const std::string& blockSelector : blockSelectors) + { + blockColorArrayMap.erase(blockSelector); + } + std::vector blockColorArrayVec; + blockColorArrayVec.reserve(blockColorArrayMap.size() * 3); + for (const auto& colorArray : blockColorArrayMap) + { + blockColorArrayVec.push_back(colorArray.first); + blockColorArrayVec.push_back(std::to_string(colorArray.second.first)); + blockColorArrayVec.push_back(colorArray.second.second); + } + // the following trace is not needed because this function is not supposed to be publicly called + // SM_SCOPED_TRACE(PropertiesModified) + // .arg("proxy", proxy) + // .arg("comment", " change block(s) color array"); + blockColorArrayNames->SetElements(blockColorArrayVec); + proxy->UpdateProperty("BlockColorArrayNames"); +} + +//---------------------------------------------------------------------------- +vtkSMColorMapEditorHelper::ColorArray vtkSMColorMapEditorHelper::GetColorArray(vtkSMProxy* proxy) +{ + const vtkSMPropertyHelper colorArray(proxy, "ColorArrayName"); + return std::make_pair( + colorArray.GetInputArrayAssociation(), colorArray.GetInputArrayNameToProcess()); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksColorArrays( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto blockColorArray = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); + if (!blockColorArray) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockColorArrayNames' property found."); + return std::vector(blockSelectors.size(), std::make_pair(-1, "")); + } + assert(blockColorArray->GetNumberOfElementsPerCommand() == 3); + std::vector colorArrays(blockSelectors.size(), std::make_pair(-1, "")); + for (unsigned int i = 0; i < blockColorArray->GetNumberOfElements(); i += 3) + { + for (unsigned int j = 0; j < blockSelectors.size(); ++j) + { + if (blockColorArray->GetElement(i) == blockSelectors[j]) + { + colorArrays[j] = std::make_pair( + std::stoi(blockColorArray->GetElement(i + 1)), blockColorArray->GetElement(i + 2)); + break; + } + } + } + return colorArrays; +} + +//---------------------------------------------------------------------------- +std::map> +vtkSMColorMapEditorHelper::GetCommonColorArraysBlockSelectors( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::map>(); + } + const std::vector colorArrays = + vtkSMColorMapEditorHelper::GetBlocksColorArrays(proxy, blockSelectors); + std::map> commonColorArrays; + for (size_t i = 0; i < blockSelectors.size(); ++i) + { + commonColorArrays[colorArrays[i]].push_back(blockSelectors[i]); + } + return commonColorArrays; +} +//---------------------------------------------------------------------------- +std::vector +vtkSMColorMapEditorHelper::GetSelectedColorArrays(vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::GetBlocksColorArrays( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::GetColorArray(proxy) }; + } +} + +//---------------------------------------------------------------------------- +vtkSMProperty* vtkSMColorMapEditorHelper::GetUseSeparateColorMapProperty(vtkSMProxy* proxy) +{ + return proxy->GetProperty("UseSeparateColorMap"); +} + +//---------------------------------------------------------------------------- +vtkSMProperty* vtkSMColorMapEditorHelper::GetBlockUseSeparateColorMapProperty(vtkSMProxy* proxy) +{ + return proxy->GetProperty("BlockUseSeparateColorMaps"); +} + +//---------------------------------------------------------------------------- +vtkSMProperty* vtkSMColorMapEditorHelper::GetSelectedUseSeparateColorMapProperty(vtkSMProxy* proxy) +{ + return this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks + ? vtkSMColorMapEditorHelper::GetBlockUseSeparateColorMapProperty(proxy) + : vtkSMColorMapEditorHelper::GetUseSeparateColorMapProperty(proxy); +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::IsUseSeparateColorMapValid(int useSeparateColorMap) +{ + return useSeparateColorMap == 0 || useSeparateColorMap == 1; +} +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetUseSeparateColorMap(vtkSMProxy* proxy, bool use) +{ + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change use separate color map"); + vtkSMPropertyHelper(proxy, "UseSeparateColorMap", true).Set(use ? 1 : 0); + proxy->UpdateProperty("UseSeparateColorMap"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetBlocksUseSeparateColorMap( + vtkSMProxy* proxy, const std::vector& blockSelectors, bool use) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockUseSeparateColorMaps = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockUseSeparateColorMaps")); + if (!blockUseSeparateColorMaps) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockUseSeparateColorMaps' property found."); + return; + } + assert(blockUseSeparateColorMaps->GetNumberOfElementsPerCommand() == 2); + std::map blockUseSeparateColorMapsMap; + for (unsigned int i = 0; i < blockUseSeparateColorMaps->GetNumberOfElements(); i += 2) + { + blockUseSeparateColorMapsMap[blockUseSeparateColorMaps->GetElement(i)] = + std::stoi(blockUseSeparateColorMaps->GetElement(i + 1)); + } + for (const std::string& blockSelector : blockSelectors) + { + blockUseSeparateColorMapsMap[blockSelector] = use; + } + std::vector blockUseSeparateColorMapsVec; + blockUseSeparateColorMapsVec.reserve(blockUseSeparateColorMapsMap.size() * 2); + for (const auto& blockUseSeparateColorMap : blockUseSeparateColorMapsMap) + { + blockUseSeparateColorMapsVec.push_back(blockUseSeparateColorMap.first); + blockUseSeparateColorMapsVec.push_back(std::to_string(blockUseSeparateColorMap.second)); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) use separate color map"); + blockUseSeparateColorMaps->SetElements(blockUseSeparateColorMapsVec); + proxy->UpdateProperty("BlockUseSeparateColorMaps"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::RemoveBlocksUseSeparateColorMaps( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockUseSeparateColorMaps = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockUseSeparateColorMaps")); + if (!blockUseSeparateColorMaps) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockUseSeparateColorMaps' property found."); + return; + } + assert(blockUseSeparateColorMaps->GetNumberOfElementsPerCommand() == 2); + std::map blockUseSeparateColorMapsMap; + for (unsigned int i = 0; i < blockUseSeparateColorMaps->GetNumberOfElements(); i += 2) + { + blockUseSeparateColorMapsMap[blockUseSeparateColorMaps->GetElement(i)] = + blockUseSeparateColorMaps->GetElement(i + 1); + } + for (const std::string& blockSelector : blockSelectors) + { + blockUseSeparateColorMapsMap.erase(blockSelector); + } + std::vector blockUseSeparateColorMapsVec; + blockUseSeparateColorMapsVec.reserve(blockUseSeparateColorMapsMap.size() * 2); + for (const auto& blockUseSeparateColorMap : blockUseSeparateColorMapsMap) + { + blockUseSeparateColorMapsVec.push_back(blockUseSeparateColorMap.first); + blockUseSeparateColorMapsVec.push_back(blockUseSeparateColorMap.second); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) use separate color map"); + blockUseSeparateColorMaps->SetElements(blockUseSeparateColorMapsVec); + proxy->UpdateProperty("BlockUseSeparateColorMaps"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetSelectedUseSeparateColorMap(vtkSMProxy* proxy, bool use) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + vtkSMColorMapEditorHelper::SetBlocksUseSeparateColorMap( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), use); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + vtkSMColorMapEditorHelper::SetUseSeparateColorMap(proxy, use); + } +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::GetUseSeparateColorMap(vtkSMProxy* proxy) +{ + return vtkSMPropertyHelper(proxy, "UseSeparateColorMap", true).GetAsInt() != 0; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksUseSeparateColorMaps( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto blockUseSeparateColorMaps = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockUseSeparateColorMaps")); + if (!blockUseSeparateColorMaps) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockUseSeparateColorMaps' property found."); + return std::vector(blockSelectors.size(), -1); + } + assert(blockUseSeparateColorMaps->GetNumberOfElementsPerCommand() == 2); + std::vector useSeparateColorMapsVec(blockSelectors.size(), -1); + for (unsigned int i = 0; i < blockUseSeparateColorMaps->GetNumberOfElements(); i += 2) + { + for (unsigned int j = 0; j < blockSelectors.size(); ++j) + { + if (blockUseSeparateColorMaps->GetElement(i) == blockSelectors[j]) + { + useSeparateColorMapsVec[j] = std::stoi(blockUseSeparateColorMaps->GetElement(i + 1)); + break; + } + } + } + return useSeparateColorMapsVec; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedUseSeparateColorMaps(vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::GetBlocksUseSeparateColorMaps( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::GetUseSeparateColorMap(proxy) }; + } +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::GetAnySelectedUseSeparateColorMap(vtkSMProxy* proxy) +{ + const std::vector useSeparateColorMaps = + vtkSMColorMapEditorHelper::GetSelectedUseSeparateColorMaps(proxy); + return std::any_of(useSeparateColorMaps.begin(), useSeparateColorMaps.end(), + [](int useSeparateColorMap) { return useSeparateColorMap == 1; }); +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::IsMapScalarsValid(int mapScalars) +{ + return mapScalars == 0 || mapScalars == 1; +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetMapScalars(vtkSMProxy* proxy, bool mapScalars) +{ + SM_SCOPED_TRACE(PropertiesModified).arg("proxy", proxy).arg("comment", " change map scalars"); + vtkSMPropertyHelper(proxy, "MapScalars", true).Set(mapScalars ? 1 : 0); + proxy->UpdateProperty("MapScalars"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetBlocksMapScalars( + vtkSMProxy* proxy, const std::vector& blockSelectors, bool mapScalars) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockMapScalars = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockMapScalars")); + if (!blockMapScalars) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockMapScalars' property found."); + return; + } + assert(blockMapScalars->GetNumberOfElementsPerCommand() == 2); + std::map blockMapScalarsMap; + for (unsigned int i = 0; i < blockMapScalars->GetNumberOfElements(); i += 2) + { + blockMapScalarsMap[blockMapScalars->GetElement(i)] = + std::stoi(blockMapScalars->GetElement(i + 1)); + } + for (const std::string& blockSelector : blockSelectors) + { + blockMapScalarsMap[blockSelector] = mapScalars; + } + std::vector blockMapScalarsVec; + blockMapScalarsVec.reserve(blockMapScalarsMap.size() * 2); + for (const auto& blockMapScalar : blockMapScalarsMap) + { + blockMapScalarsVec.push_back(blockMapScalar.first); + blockMapScalarsVec.push_back(std::to_string(blockMapScalar.second)); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) map scalars"); + blockMapScalars->SetElements(blockMapScalarsVec); + proxy->UpdateProperty("BlockMapScalars"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::RemoveBlocksMapScalars( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockMapScalars = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockMapScalars")); + if (!blockMapScalars) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockMapScalars' property found."); + return; + } + assert(blockMapScalars->GetNumberOfElementsPerCommand() == 2); + std::map blockMapScalarsMap; + for (unsigned int i = 0; i < blockMapScalars->GetNumberOfElements(); i += 2) + { + blockMapScalarsMap[blockMapScalars->GetElement(i)] = blockMapScalars->GetElement(i + 1); + } + for (const std::string& blockSelector : blockSelectors) + { + blockMapScalarsMap.erase(blockSelector); + } + std::vector blockMapScalarsVec; + blockMapScalarsVec.reserve(blockMapScalarsMap.size() * 2); + for (const auto& blockMapScalar : blockMapScalarsMap) + { + blockMapScalarsVec.push_back(blockMapScalar.first); + blockMapScalarsVec.push_back(blockMapScalar.second); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) map scalars"); + blockMapScalars->SetElements(blockMapScalarsVec); + proxy->UpdateProperty("BlockMapScalars"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetSelectedMapScalars(vtkSMProxy* proxy, bool mapScalars) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + vtkSMColorMapEditorHelper::SetBlocksMapScalars( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), mapScalars); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + vtkSMColorMapEditorHelper::SetMapScalars(proxy, mapScalars); + } +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::GetMapScalars(vtkSMProxy* proxy) +{ + return vtkSMPropertyHelper(proxy, "MapScalars", true).GetAsInt() != 0; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksMapScalars( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto blockMapScalars = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockMapScalars")); + if (!blockMapScalars) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockMapScalars' property found."); + return std::vector(blockSelectors.size(), -1); + } + assert(blockMapScalars->GetNumberOfElementsPerCommand() == 2); + std::vector blockMapScalarsVec(blockSelectors.size(), -1); + for (unsigned int i = 0; i < blockMapScalars->GetNumberOfElements(); i += 2) + { + for (unsigned int j = 0; j < blockSelectors.size(); ++j) + { + if (blockMapScalars->GetElement(i) == blockSelectors[j]) + { + blockMapScalarsVec[j] = std::stoi(blockMapScalars->GetElement(i + 1)); + break; + } + } + } + return blockMapScalarsVec; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedMapScalars(vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::GetBlocksMapScalars( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::GetMapScalars(proxy) }; + } +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::GetAnySelectedMapScalars(vtkSMProxy* proxy) +{ + const std::vector mapScalars = vtkSMColorMapEditorHelper::GetSelectedMapScalars(proxy); + return std::any_of( + mapScalars.begin(), mapScalars.end(), [](int mapScalar) { return mapScalar == 1; }); +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::IsInterpolateScalarsBeforeMappingValid(int interpolate) +{ + return interpolate == 0 || interpolate == 1; +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetInterpolateScalarsBeforeMapping( + vtkSMProxy* proxy, bool interpolateScalars) +{ + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change interpolate scalars before mapping"); + vtkSMPropertyHelper(proxy, "InterpolateScalarsBeforeMapping", true) + .Set(interpolateScalars ? 1 : 0); + proxy->UpdateProperty("InterpolateScalarsBeforeMapping"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetBlocksInterpolateScalarsBeforeMapping( + vtkSMProxy* proxy, const std::vector& blockSelectors, bool interpolate) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockInterpolateScalarsBeforeMapping = vtkSMStringVectorProperty::SafeDownCast( + proxy->GetProperty("BlockInterpolateScalarsBeforeMappings")); + if (!blockInterpolateScalarsBeforeMapping) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockInterpolateScalarsBeforeMappings' property found."); + return; + } + assert(blockInterpolateScalarsBeforeMapping->GetNumberOfElementsPerCommand() == 2); + std::map blockInterpolateScalarsBeforeMappingMap; + for (unsigned int i = 0; i < blockInterpolateScalarsBeforeMapping->GetNumberOfElements(); i += 2) + { + blockInterpolateScalarsBeforeMappingMap[blockInterpolateScalarsBeforeMapping->GetElement(i)] = + std::stoi(blockInterpolateScalarsBeforeMapping->GetElement(i + 1)); + } + for (const std::string& blockSelector : blockSelectors) + { + blockInterpolateScalarsBeforeMappingMap[blockSelector] = interpolate; + } + std::vector blockInterpolateScalarsBeforeMappingVec; + blockInterpolateScalarsBeforeMappingVec.reserve( + blockInterpolateScalarsBeforeMappingMap.size() * 2); + for (const auto& blockInterpolate : blockInterpolateScalarsBeforeMappingMap) + { + blockInterpolateScalarsBeforeMappingVec.push_back(blockInterpolate.first); + blockInterpolateScalarsBeforeMappingVec.push_back(std::to_string(blockInterpolate.second)); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) interpolate scalars before mapping"); + blockInterpolateScalarsBeforeMapping->SetElements(blockInterpolateScalarsBeforeMappingVec); + proxy->UpdateProperty("BlockInterpolateScalarsBeforeMappings"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::RemoveBlocksInterpolateScalarsBeforeMappings( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockInterpolateScalarsBeforeMapping = vtkSMStringVectorProperty::SafeDownCast( + proxy->GetProperty("BlockInterpolateScalarsBeforeMappings")); + if (!blockInterpolateScalarsBeforeMapping) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockInterpolateScalarsBeforeMappings' property found."); + return; + } + assert(blockInterpolateScalarsBeforeMapping->GetNumberOfElementsPerCommand() == 2); + std::map blockInterpolateScalarsBeforeMappingMap; + for (unsigned int i = 0; i < blockInterpolateScalarsBeforeMapping->GetNumberOfElements(); i += 2) + { + blockInterpolateScalarsBeforeMappingMap[blockInterpolateScalarsBeforeMapping->GetElement(i)] = + std::stoi(blockInterpolateScalarsBeforeMapping->GetElement(i + 1)); + } + for (const std::string& blockSelector : blockSelectors) + { + blockInterpolateScalarsBeforeMappingMap.erase(blockSelector); + } + std::vector blockInterpolateScalarsBeforeMappingVec; + blockInterpolateScalarsBeforeMappingVec.reserve( + blockInterpolateScalarsBeforeMappingMap.size() * 2); + for (const auto& blockInterpolate : blockInterpolateScalarsBeforeMappingMap) + { + blockInterpolateScalarsBeforeMappingVec.push_back(blockInterpolate.first); + blockInterpolateScalarsBeforeMappingVec.push_back(std::to_string(blockInterpolate.second)); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) interpolate scalars before mapping"); + blockInterpolateScalarsBeforeMapping->SetElements(blockInterpolateScalarsBeforeMappingVec); + proxy->UpdateProperty("BlockInterpolateScalarsBeforeMappings"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetSelectedInterpolateScalarsBeforeMapping( + vtkSMProxy* proxy, bool interpolateScalars) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + vtkSMColorMapEditorHelper::SetBlocksInterpolateScalarsBeforeMapping( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), interpolateScalars); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + vtkSMColorMapEditorHelper::SetInterpolateScalarsBeforeMapping(proxy, interpolateScalars); + } +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::GetInterpolateScalarsBeforeMapping(vtkSMProxy* proxy) +{ + return vtkSMPropertyHelper(proxy, "InterpolateScalarsBeforeMapping", true).GetAsInt() != 0; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksInterpolateScalarsBeforeMappings( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto blockInterpolateScalarsBeforeMapping = vtkSMStringVectorProperty::SafeDownCast( + proxy->GetProperty("BlockInterpolateScalarsBeforeMappings")); + if (!blockInterpolateScalarsBeforeMapping) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockInterpolateScalarsBeforeMappings' property found."); + return std::vector(blockSelectors.size(), -1); + } + assert(blockInterpolateScalarsBeforeMapping->GetNumberOfElementsPerCommand() == 2); + std::vector interpolateScalarsBeforeMappingVec(blockSelectors.size(), -1); + for (unsigned int i = 0; i < blockInterpolateScalarsBeforeMapping->GetNumberOfElements(); i += 2) + { + for (unsigned int j = 0; j < blockSelectors.size(); ++j) + { + if (blockInterpolateScalarsBeforeMapping->GetElement(i) == blockSelectors[j]) + { + interpolateScalarsBeforeMappingVec[j] = + std::stoi(blockInterpolateScalarsBeforeMapping->GetElement(i + 1)); + break; + } + } + } + return interpolateScalarsBeforeMappingVec; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedInterpolateScalarsBeforeMappings( + vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::GetBlocksInterpolateScalarsBeforeMappings( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::GetInterpolateScalarsBeforeMapping(proxy) }; + } +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::GetAnySelectedInterpolateScalarsBeforeMapping(vtkSMProxy* proxy) +{ + const std::vector interpolateScalarsBeforeMappings = + vtkSMColorMapEditorHelper::GetSelectedInterpolateScalarsBeforeMappings(proxy); + return std::any_of(interpolateScalarsBeforeMappings.begin(), + interpolateScalarsBeforeMappings.end(), + [](int interpolateScalarsBeforeMapping) { return interpolateScalarsBeforeMapping == 1; }); +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::IsOpacityValid(double opacity) +{ + return opacity >= 0.0 && opacity <= 1.0; +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetOpacity(vtkSMProxy* proxy, double opacity) +{ + if (!vtkSMColorMapEditorHelper::IsOpacityValid(opacity)) + { + vtkWarningWithObjectMacro(proxy, "Invalid opacity."); + return; + } + SM_SCOPED_TRACE(PropertiesModified).arg("proxy", proxy).arg("comment", " change opacity"); + vtkSMPropertyHelper(proxy, "Opacity", true).Set(opacity); + proxy->UpdateProperty("Opacity"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetBlocksOpacity( + vtkSMProxy* proxy, const std::vector& blockSelectors, double opacity) +{ + if (blockSelectors.empty()) + { + return; + } + if (!vtkSMColorMapEditorHelper::IsOpacityValid(opacity)) + { + vtkWarningWithObjectMacro(proxy, "Invalid opacity."); + return; + } + auto blockOpacities = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockOpacities")); + if (!blockOpacities) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockOpacities' property found."); + return; + } + assert(blockOpacities->GetNumberOfElementsPerCommand() == 2); + std::map blockOpacitiesMap; + for (unsigned int i = 0; i < blockOpacities->GetNumberOfElements(); i += 2) + { + blockOpacitiesMap[blockOpacities->GetElement(i)] = std::stod(blockOpacities->GetElement(i + 1)); + } + for (const std::string& blockSelector : blockSelectors) + { + blockOpacitiesMap[blockSelector] = opacity; + } + std::vector blockOpacitiesVec; + blockOpacitiesVec.reserve(blockOpacitiesMap.size() * 2); + for (const auto& blockOpacity : blockOpacitiesMap) + { + blockOpacitiesVec.push_back(blockOpacity.first); + blockOpacitiesVec.push_back(std::to_string(blockOpacity.second)); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) opacity"); + blockOpacities->SetElements(blockOpacitiesVec); + proxy->UpdateProperty("BlockOpacities"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::RemoveBlocksOpacities( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockOpacities = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockOpacities")); + if (!blockOpacities) { - vtkWarningWithObjectMacro( - proxy, "Missing 'BlockLookupTableSelectors' or 'BlockLookupTables' property."); + vtkDebugWithObjectMacro(proxy, "No 'BlockOpacities' property found."); return; } - bool found = false; - for (unsigned int i = 0; i < blockLUTSelectors->GetNumberOfElements(); ++i) + assert(blockOpacities->GetNumberOfElementsPerCommand() == 2); + std::map blockOpacitiesMap; + for (unsigned int i = 0; i < blockOpacities->GetNumberOfElements(); i += 2) + { + blockOpacitiesMap[blockOpacities->GetElement(i)] = std::stod(blockOpacities->GetElement(i + 1)); + } + for (const std::string& blockSelector : blockSelectors) + { + blockOpacitiesMap.erase(blockSelector); + } + std::vector blockOpacitiesVec; + blockOpacitiesVec.reserve(blockOpacitiesMap.size() * 2); + for (const auto& blockOpacity : blockOpacitiesMap) + { + blockOpacitiesVec.push_back(blockOpacity.first); + blockOpacitiesVec.push_back(std::to_string(blockOpacity.second)); + } + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", proxy) + .arg("comment", " change block(s) opacity"); + blockOpacities->SetElements(blockOpacitiesVec); + proxy->UpdateProperty("BlockOpacities"); +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetSelectedOpacity(vtkSMProxy* proxy, double opacity) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + vtkSMColorMapEditorHelper::SetBlocksOpacity( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), opacity); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + vtkSMColorMapEditorHelper::SetOpacity(proxy, opacity); + } +} + +//---------------------------------------------------------------------------- +double vtkSMColorMapEditorHelper::GetOpacity(vtkSMProxy* proxy) +{ + return vtkSMPropertyHelper(proxy, "Opacity", true).GetAsDouble(); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksOpacities( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto blockOpacities = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockOpacities")); + if (!blockOpacities) + { + vtkDebugWithObjectMacro(proxy, "No 'BlockOpacities' property found."); + return std::vector(blockSelectors.size(), VTK_DOUBLE_MAX); + } + assert(blockOpacities->GetNumberOfElementsPerCommand() == 2); + std::vector blockOpacitiesVec(blockSelectors.size(), VTK_DOUBLE_MAX); + for (unsigned int i = 0; i < blockOpacities->GetNumberOfElements(); i += 2) { - if (blockLUTSelectors->GetElement(i) == blockSelector) + for (unsigned int j = 0; j < blockSelectors.size(); ++j) { - blockLUTs->SetProxy(i, lutProxy); - found = true; + if (blockOpacities->GetElement(i) == blockSelectors[j]) + { + blockOpacitiesVec[j] = std::stod(blockOpacities->GetElement(i + 1)); + break; + } } } - if (!found) + return blockOpacitiesVec; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedOpacities(vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::GetBlocksOpacities( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation { - blockLUTSelectors->AppendElements({ blockSelector }); - blockLUTs->AddProxy(lutProxy); + return { vtkSMColorMapEditorHelper::GetOpacity(proxy) }; } } //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMColorMapEditorHelper::GetBlockLookupTable( - vtkSMProxy* proxy, const std::string& blockSelector) +void vtkSMColorMapEditorHelper::ResetBlocksProperties(vtkSMProxy* proxy, + const std::vector& blockSelectors, const std::vector& propertyNames) { - auto blockLUTSelectors = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockLookupTableSelectors")); - auto blockLUTs = vtkSMProxyProperty::SafeDownCast(proxy->GetProperty("BlockLookupTables")); - if (!blockLUTSelectors || !blockLUTs) + if (blockSelectors.empty()) { - vtkWarningWithObjectMacro( - proxy, "Missing 'BlockLookupTableSelectors' or 'BlockLookupTables' property."); - return nullptr; + return; + } + if (propertyNames.empty()) + { + return; + } + std::vector removedPropertyNames; + if (std::find(propertyNames.begin(), propertyNames.end(), "BlockColors") != propertyNames.end()) + { + vtkSMColorMapEditorHelper::RemoveBlocksColors(proxy, blockSelectors); + removedPropertyNames.push_back("BlockColors"); } - for (unsigned int i = 0; i < blockLUTSelectors->GetNumberOfElements(); ++i) + if (std::find(propertyNames.begin(), propertyNames.end(), "BlockColorArrayNames") != + propertyNames.end() || + std::find(propertyNames.begin(), propertyNames.end(), "BlockLookupTables") != + propertyNames.end()) { - if (blockLUTSelectors->GetElement(i) == blockSelector) + vtkSMColorMapEditorHelper::SetBlocksScalarColoring(proxy, blockSelectors, nullptr, -1); + removedPropertyNames.push_back("BlockColorArrayNames"); + removedPropertyNames.push_back("BlockLookupTables"); + } + if (std::find(propertyNames.begin(), propertyNames.end(), "BlockUseSeparateColorMaps") != + propertyNames.end()) + { + vtkSMColorMapEditorHelper::RemoveBlocksUseSeparateColorMaps(proxy, blockSelectors); + removedPropertyNames.push_back("BlockUseSeparateColorMaps"); + removedPropertyNames.push_back("BlockColorArrayNames"); + } + if (std::find(propertyNames.begin(), propertyNames.end(), "BlockMapScalars") != + propertyNames.end()) + { + vtkSMColorMapEditorHelper::RemoveBlocksMapScalars(proxy, blockSelectors); + removedPropertyNames.push_back("BlockMapScalars"); + } + if (std::find(propertyNames.begin(), propertyNames.end(), + "BlockInterpolateScalarsBeforeMappings") != propertyNames.end()) + { + vtkSMColorMapEditorHelper::RemoveBlocksInterpolateScalarsBeforeMappings(proxy, blockSelectors); + removedPropertyNames.push_back("BlockInterpolateScalarsBeforeMappings"); + } + if (std::find(propertyNames.begin(), propertyNames.end(), "BlockOpacities") != + propertyNames.end()) + { + vtkSMColorMapEditorHelper::RemoveBlocksOpacities(proxy, blockSelectors); + removedPropertyNames.push_back("BlockOpacities"); + } + std::vector notRemovedPropertyNames; + for (const std::string& propertyName : propertyNames) + { + if (std::find(removedPropertyNames.begin(), removedPropertyNames.end(), propertyName) == + removedPropertyNames.end()) { - return blockLUTs->GetProxy(i); + notRemovedPropertyNames.push_back(propertyName); } } - return nullptr; + for (const std::string& propertyName : notRemovedPropertyNames) + { + vtkWarningWithObjectMacro(proxy, "Property name: " << propertyName << " could not be removed"); + } +} + +//---------------------------------------------------------------------------- +void vtkSMColorMapEditorHelper::SetBlocksLookupTable( + vtkSMProxy* proxy, const std::vector& blockSelectors, vtkSMProxy* lut) +{ + if (blockSelectors.empty()) + { + return; + } + auto blockColorArrayNames = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); + auto blockLUTs = vtkSMProxyProperty::SafeDownCast(proxy->GetProperty("BlockLookupTables")); + if (!blockColorArrayNames || !blockLUTs) + { + vtkDebugWithObjectMacro( + proxy, "No 'BlockColorArrayNames' or 'BlockLookupTables' properties found."); + return; + } + std::map blockLUTsMap; + for (unsigned int i = 0; i < blockLUTs->GetNumberOfProxies(); ++i) + { + blockLUTsMap[blockColorArrayNames->GetElement(3 * i)] = blockLUTs->GetProxy(i); + } + for (const std::string& blockSelector : blockSelectors) + { + blockLUTsMap[blockSelector] = lut; + } + std::vector blockLUTsVec; + blockLUTsVec.reserve(blockLUTsMap.size()); + for (const auto& blockLUT : blockLUTsMap) + { + blockLUTsVec.push_back(blockLUT.second); + } + // the following trace is not needed because this function is not supposed to be publicly called + // SM_SCOPED_TRACE(PropertiesModified).arg("proxy", proxy); + blockLUTs->SetProxies(static_cast(blockLUTsVec.size()), blockLUTsVec.data()); + proxy->UpdateProperty("BlockLookupTables"); } //---------------------------------------------------------------------------- -void vtkSMColorMapEditorHelper::RemoveBlockLookupTable( - vtkSMProxy* proxy, const std::string& blockSelector) +void vtkSMColorMapEditorHelper::RemoveBlocksLookupTables( + vtkSMProxy* proxy, const std::vector& blockSelectors) { - auto blockLUTSelectors = - vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockLookupTableSelectors")); + if (blockSelectors.empty()) + { + return; + } + auto blockColorArrayNames = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); auto blockLUTs = vtkSMProxyProperty::SafeDownCast(proxy->GetProperty("BlockLookupTables")); - if (!blockLUTSelectors || !blockLUTs) + if (!blockColorArrayNames || !blockLUTs) { - vtkWarningWithObjectMacro( - proxy, "Missing 'BlockLookupTableSelectors' or 'BlockLookupTables' property."); + vtkDebugWithObjectMacro( + proxy, "No 'BlockColorArrayNames' or 'BlockLookupTables' properties found."); return; } - // find selector (if any) and remove the selector and the lut - bool found = false; - unsigned int selectorIdx = 0; - for (unsigned int i = 0; i < blockLUTSelectors->GetNumberOfElements(); ++i) + std::map blockLUTsMap; + for (unsigned int i = 0; i < blockLUTs->GetNumberOfProxies(); ++i) { - if (blockLUTSelectors->GetElement(i) == blockSelector) - { - selectorIdx = i; - found = true; - break; - } + blockLUTsMap[blockColorArrayNames->GetElement(3 * i)] = blockLUTs->GetProxy(i); + } + for (const std::string& blockSelector : blockSelectors) + { + blockLUTsMap.erase(blockSelector); + } + std::vector blockLUTsVec; + blockLUTsVec.reserve(blockLUTsMap.size()); + for (const auto& blockLUT : blockLUTsMap) + { + blockLUTsVec.push_back(blockLUT.second); + } + // the following trace is not needed because this function is not supposed to be publicly called + // SM_SCOPED_TRACE(PropertiesModified).arg("proxy", proxy); + blockLUTs->SetProxies(static_cast(blockLUTsVec.size()), blockLUTsVec.data()); + proxy->UpdateProperty("BlockLookupTables"); +} + +//---------------------------------------------------------------------------- +vtkSMProxy* vtkSMColorMapEditorHelper::GetLookupTable(vtkSMProxy* proxy) +{ + vtkSMProperty* lutProperty = proxy->GetProperty("LookupTable"); + if (!lutProperty) + { + vtkGenericWarningMacro("Missing 'LookupTable' property."); + return nullptr; + } + + const vtkSMPropertyHelper lutPropertyHelper(lutProperty); + if (lutPropertyHelper.GetNumberOfElements() == 0) + { + return nullptr; + } + + return lutPropertyHelper.GetAsProxy(); +} + +//---------------------------------------------------------------------------- +vtkSMProxy* vtkSMColorMapEditorHelper::GetLookupTable(vtkSMProxy* proxy, vtkSMProxy* view) +{ + if (!view) + { + return nullptr; + } + return vtkSMColorMapEditorHelper::GetLookupTable(proxy); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksLookupTables( + vtkSMProxy* proxy, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + const auto blockColorArrayNames = + vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("BlockColorArrayNames")); + const auto blockLUTs = vtkSMProxyProperty::SafeDownCast(proxy->GetProperty("BlockLookupTables")); + if (!blockColorArrayNames || !blockLUTs) + { + vtkDebugWithObjectMacro( + proxy, "No 'BlockColorArrayNames' or 'BlockLookupTables' properties found."); + return std::vector(blockSelectors.size(), nullptr); } - if (found) + std::vector blockLUTsVec(blockSelectors.size(), nullptr); + for (unsigned int i = 0; i < blockLUTs->GetNumberOfProxies(); ++i) { - std::vector blockLUTSelectorsVec; - std::vector blockLUTsVec; - for (unsigned int i = 0; i < blockLUTSelectors->GetNumberOfElements(); ++i) + for (size_t j = 0; j < blockSelectors.size(); ++j) { - if (i != selectorIdx) + if (blockColorArrayNames->GetElement(3 * i) == blockSelectors[j]) { - blockLUTSelectorsVec.push_back(blockLUTSelectors->GetElement(i)); - blockLUTsVec.push_back(blockLUTs->GetProxy(i)); + blockLUTsVec[j] = blockLUTs->GetProxy(i); + break; } } - blockLUTSelectors->SetElements(blockLUTSelectorsVec); - blockLUTs->SetProxies(static_cast(blockLUTsVec.size()), blockLUTsVec.data()); } + return blockLUTsVec; +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetBlocksLookupTables( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors) +{ + if (blockSelectors.empty()) + { + return std::vector(); + } + if (!view) + { + return std::vector(blockSelectors.size(), nullptr); + } + return vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, blockSelectors); +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedLookupTables(vtkSMProxy* proxy) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::GetBlocksLookupTables( + proxy, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::GetLookupTable(proxy) }; + } +} + +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedLookupTables( + vtkSMProxy* proxy, vtkSMProxy* view) +{ + if (!view) + { + return {}; + } + return vtkSMColorMapEditorHelper::GetSelectedLookupTables(proxy, view); } //---------------------------------------------------------------------------- @@ -1433,9 +2969,9 @@ int vtkSMColorMapEditorHelper::IsScalarBarStickyVisible(vtkSMProxy* proxy, vtkSM vtkGenericWarningMacro("Failed to determine the LookupTable being used."); return -1; } - vtkSMProxy* lutProxy = lutPropertyHelper.GetAsProxy(); + vtkSMProxy* lut = lutPropertyHelper.GetAsProxy(); auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lutProxy, view)); + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lut, view)); if (sbProxy) { const vtkSMPropertyHelper sbsvPropertyHelper(sbProxy->GetProperty("StickyVisible")); @@ -1447,28 +2983,44 @@ int vtkSMColorMapEditorHelper::IsScalarBarStickyVisible(vtkSMProxy* proxy, vtkSM } //---------------------------------------------------------------------------- -int vtkSMColorMapEditorHelper::IsBlockScalarBarStickyVisible( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) +std::vector vtkSMColorMapEditorHelper::IsBlocksScalarBarStickyVisible( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors) { - if (!view) + if (blockSelectors.empty()) { - return -1; + return std::vector(); } - vtkSMProxy* blockLUTProxy = vtkSMColorMapEditorHelper::GetBlockLookupTable(proxy, blockSelector); - if (!blockLUTProxy) + if (!view) { - return -1; + return std::vector(blockSelectors.size(), -1); } - auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(blockLUTProxy, view)); - if (sbProxy) + const std::vector blockLuts = + vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, blockSelectors); + + std::vector blockStickyVisibleVec(blockSelectors.size()); + for (unsigned int i = 0; i < blockSelectors.size(); ++i) { - const vtkSMPropertyHelper sbsvPropertyHelper(sbProxy->GetProperty("StickyVisible")); - return sbsvPropertyHelper.GetNumberOfElements() - ? vtkSMPropertyHelper(sbProxy, "StickyVisible").GetAsInt() - : -1; + if (vtkSMProxy* blockLut = blockLuts[i]) + { + if (auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(blockLut, view))) + { + const vtkSMPropertyHelper sbsvPropertyHelper(sbProxy->GetProperty("StickyVisible")); + blockStickyVisibleVec[i] = sbsvPropertyHelper.GetNumberOfElements() + ? vtkSMPropertyHelper(sbProxy, "StickyVisible").GetAsInt() + : -1; + } + else + { + blockStickyVisibleVec[i] = -1; + } + } + else + { + blockStickyVisibleVec[i] = -1; + } } - return -1; + return blockStickyVisibleVec; } //---------------------------------------------------------------------------- @@ -1486,88 +3038,29 @@ bool vtkSMColorMapEditorHelper::UpdateScalarBarRange( vtkSMProperty* lutProperty = proxy->GetProperty("LookupTable"); if (!lutProperty) { - vtkGenericWarningMacro("Missing 'LookupTable' property"); - return false; - } - - const vtkSMPropertyHelper lutPropertyHelper(lutProperty); - vtkSMProxy* lutProxy = usingScalarBarColoring ? lutPropertyHelper.GetAsProxy() - : vtkSMColorMapEditorHelper::GetLastLUTProxy(proxy); - - if (!lutProxy) - { - return false; - } - - auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lutProxy, view)); - - if (!sbProxy) - { - return false; - } - - vtkNew mgr; - vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(lutProxy, view); - - vtkSMProperty* maxRangeProp = sbSMProxy->GetProperty("DataRangeMax"); - vtkSMProperty* minRangeProp = sbSMProxy->GetProperty("DataRangeMin"); - - if (minRangeProp && maxRangeProp) - { - vtkSMPropertyHelper minRangePropHelper(minRangeProp); - vtkSMPropertyHelper maxRangePropHelper(maxRangeProp); - - // We remove the range that was potentially previously stored - sbProxy->RemoveRange(repr); - - // If we do not want to delete proxy range, then we update it with its potential new value. - if (!deleteRange) - { - sbProxy->AddRange(repr); - } - - double updatedRange[2]; - sbProxy->GetRange(updatedRange); - minRangePropHelper.Set(updatedRange[0]); - maxRangePropHelper.Set(updatedRange[1]); - } - sbSMProxy->UpdateVTKObjects(); - - vtkSMColorMapEditorHelper::SetLastLUTProxy(proxy, usingScalarBarColoring ? lutProxy : nullptr); - return true; -} - -//---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::UpdateBlockScalarBarRange( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector, bool deleteRange) -{ - auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); - if (!repr) - { + vtkGenericWarningMacro("Missing 'LookupTable' property"); return false; } - const bool usingScalarBarColoring = - vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(repr, blockSelector); + const vtkSMPropertyHelper lutPropertyHelper(lutProperty); + vtkSMProxy* lut = usingScalarBarColoring ? lutPropertyHelper.GetAsProxy() + : vtkSMColorMapEditorHelper::GetLastLookupTable(proxy); - vtkSMProxy* lutProxy = usingScalarBarColoring - ? vtkSMColorMapEditorHelper::GetBlockLookupTable(repr, blockSelector) - : vtkSMColorMapEditorHelper::GetLastBlockLUTProxy(repr, blockSelector); - if (!lutProxy) + if (!lut) { return false; } auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lutProxy, view)); + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lut, view)); + if (!sbProxy) { return false; } vtkNew mgr; - vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(lutProxy, view); + vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(lut, view); vtkSMProperty* maxRangeProp = sbSMProxy->GetProperty("DataRangeMax"); vtkSMProperty* minRangeProp = sbSMProxy->GetProperty("DataRangeMin"); @@ -1578,12 +3071,12 @@ bool vtkSMColorMapEditorHelper::UpdateBlockScalarBarRange( vtkSMPropertyHelper maxRangePropHelper(maxRangeProp); // We remove the range that was potentially previously stored - sbProxy->RemoveBlockRange(repr, blockSelector); + sbProxy->RemoveRange(repr); - // If we do not want to delete this range, then we update it with its potential new value. + // If we do not want to delete proxy range, then we update it with its potential new value. if (!deleteRange) { - sbProxy->AddBlockRange(repr, blockSelector); + sbProxy->AddRange(repr); } double updatedRange[2]; @@ -1593,44 +3086,82 @@ bool vtkSMColorMapEditorHelper::UpdateBlockScalarBarRange( } sbSMProxy->UpdateVTKObjects(); - vtkSMColorMapEditorHelper::SetLastBlockLUTProxy( - repr, blockSelector, usingScalarBarColoring ? lutProxy : nullptr); + vtkSMColorMapEditorHelper::SetLastLookupTable(proxy, usingScalarBarColoring ? lut : nullptr); return true; } //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMColorMapEditorHelper::GetLUTProxy(vtkSMProxy* proxy, vtkSMProxy* view) +std::vector vtkSMColorMapEditorHelper::UpdateBlocksScalarBarRange( + vtkSMProxy* proxy, vtkSMProxy* view, bool deleteRange) { - if (!view) + auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); + if (!repr) { - return nullptr; + return {}; } + // get the selectors of the colored blocks + const std::vector blockSelectors = + vtkSMColorMapEditorHelper::GetColorArraysBlockSelectors(proxy); - vtkSMProperty* lutProperty = proxy->GetProperty("LookupTable"); - if (!lutProperty) - { - vtkGenericWarningMacro("Missing 'LookupTable' property."); - return nullptr; - } + const std::vector blocksUsingScalarColoring = + vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(repr, blockSelectors); - const vtkSMPropertyHelper lutPropertyHelper(lutProperty); - if (lutPropertyHelper.GetNumberOfElements() == 0) + std::vector blockLuts = + vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, blockSelectors); + std::vector lastblockLuts = + vtkSMColorMapEditorHelper::GetLastBlocksLookupTables(repr, blockSelectors); + for (unsigned int i = 0; i < blockSelectors.size(); ++i) { - return nullptr; + blockLuts[i] = blocksUsingScalarColoring[i] ? blockLuts[i] : lastblockLuts[i]; } + vtkNew mgr; + std::vector updatedRanges(blockSelectors.size(), false); + for (unsigned int i = 0; i < blockSelectors.size(); ++i) + { + if (!blockLuts[i]) + { + updatedRanges[i] = false; + continue; + } - return lutPropertyHelper.GetAsProxy(); -} + auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(blockLuts[i], view)); + if (!sbProxy) + { + updatedRanges[i] = false; + continue; + } -//---------------------------------------------------------------------------- -vtkSMProxy* vtkSMColorMapEditorHelper::GetBlockLUTProxy( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) -{ - if (!view) - { - return nullptr; + vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(blockLuts[i], view); + + vtkSMProperty* maxRangeProp = sbSMProxy->GetProperty("DataRangeMax"); + vtkSMProperty* minRangeProp = sbSMProxy->GetProperty("DataRangeMin"); + + if (minRangeProp && maxRangeProp) + { + vtkSMPropertyHelper minRangePropHelper(minRangeProp); + vtkSMPropertyHelper maxRangePropHelper(maxRangeProp); + + // We remove the range that was potentially previously stored + sbProxy->RemoveBlockRange(repr, blockSelectors[i]); + + // If we do not want to delete this range, then we update it with its potential new value. + if (!deleteRange) + { + sbProxy->AddBlockRange(repr, blockSelectors[i]); + } + + double updatedRange[2]; + sbProxy->GetRange(updatedRange); + minRangePropHelper.Set(updatedRange[0]); + maxRangePropHelper.Set(updatedRange[1]); + } + sbSMProxy->UpdateVTKObjects(); + vtkSMColorMapEditorHelper::SetLastBlockLookupTable( + repr, blockSelectors[i], blocksUsingScalarColoring[i] ? blockLuts[i] : nullptr); + updatedRanges[i] = true; } - return vtkSMColorMapEditorHelper::GetBlockLookupTable(proxy, blockSelector); + return updatedRanges; } //---------------------------------------------------------------------------- @@ -1643,8 +3174,8 @@ bool vtkSMColorMapEditorHelper::SetScalarBarVisibility( return false; } - vtkSMProxy* lutProxy = GetLUTProxy(proxy, view); - if (!lutProxy) + vtkSMProxy* lut = vtkSMColorMapEditorHelper::GetLookupTable(proxy, view); + if (!lut) { vtkGenericWarningMacro("Failed to determine the LookupTable being used."); return false; @@ -1659,18 +3190,18 @@ bool vtkSMColorMapEditorHelper::SetScalarBarVisibility( // If the lut proxy changed, we need to remove ourself (representation proxy) // from the scalar bar widget that we used to be linked to. - vtkSMProxy* lastLUTProxy = vtkSMColorMapEditorHelper::GetLastLUTProxy(proxy); - if (lastLUTProxy && lutProxy != lastLUTProxy) + vtkSMProxy* lastLut = vtkSMColorMapEditorHelper::GetLastLookupTable(proxy); + if (lastLut && lut != lastLut) { if (auto lastSBProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lastLUTProxy, view))) + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lastLut, view))) { lastSBProxy->RemoveRange(repr); } } auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lutProxy, view)); + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lut, view)); if (sbProxy) { @@ -1712,7 +3243,7 @@ bool vtkSMColorMapEditorHelper::SetScalarBarVisibility( } vtkNew mgr; - vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(lutProxy, view); + vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(lut, view); if (!sbSMProxy) { vtkGenericWarningMacro("Failed to locate/create ScalarBar representation."); @@ -1759,133 +3290,166 @@ bool vtkSMColorMapEditorHelper::SetScalarBarVisibility( } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::SetBlockScalarBarVisibility( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector, bool visible) +std::vector vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors, bool visible) { + if (blockSelectors.empty()) + { + return std::vector(); + } auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); if (!repr || !view) { - return false; + return std::vector(blockSelectors.size(), false); } - vtkSMProxy* lutBlockProxy = - vtkSMColorMapEditorHelper::GetBlockLUTProxy(repr, view, blockSelector); - if (!lutBlockProxy) + const std::vector blockLuts = + vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, view, blockSelectors); + if (std::all_of(blockLuts.begin(), blockLuts.end(), [](vtkSMProxy* lut) { return !lut; })) { - return false; + vtkGenericWarningMacro("Failed to determine the LookupTables being used."); + return std::vector(blockSelectors.size(), false); } SM_SCOPED_TRACE(CallMethod) .arg(repr) - .arg("SetBlockScalarBarVisibility") + .arg("SetBlocksScalarBarVisibility") .arg(view) - .arg(blockSelector.c_str()) + .arg(blockSelectors) .arg(visible) .arg("comment", - visible ? "show block color bar/color legend" : "hide block color bar/color legend"); + visible ? "show block(s) color bar/color legend" : "hide block color bar/color legend"); - vtkSMProxy* lastBlockLUTProxy = - vtkSMColorMapEditorHelper::GetLastBlockLUTProxy(repr, blockSelector); - - // If the lut proxy changed, we need to remove ourself (representation proxy) - // from the scalar bar widget that we used to be linked to. - if (lastBlockLUTProxy && lutBlockProxy != lastBlockLUTProxy) - { - if (auto lastSBProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lastBlockLUTProxy, view))) + const std::vector lastBlockLuts = + vtkSMColorMapEditorHelper::GetLastBlocksLookupTables(repr, blockSelectors); + const std::vector blocksUsingScalarColoring = + vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(repr, blockSelectors); + vtkNew mgr; + const std::vector blockColorArrayNames = + vtkSMColorMapEditorHelper::GetBlocksColorArrays(proxy, blockSelectors); + + std::vector updatedRanges(blockSelectors.size(), false); + for (size_t i = 0; i < blockSelectors.size(); ++i) + { + const std::string& blockSelector = blockSelectors[i]; + vtkSMProxy* blockLut = blockLuts[i]; + vtkSMProxy* lastBlockLut = lastBlockLuts[i]; + // If the lut proxy changed, we need to remove ourself (representation proxy) + // from the scalar bar widget that we used to be linked to. + if (lastBlockLut && blockLut != lastBlockLut) { - lastSBProxy->RemoveBlockRange(repr, blockSelector); + if (auto lastSBProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lastBlockLut, view))) + { + lastSBProxy->RemoveBlockRange(repr, blockSelector); + } } - } + auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(blockLut, view)); - auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( - vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lutBlockProxy, view)); + if (sbProxy) + { + const int legacyVisible = vtkSMPropertyHelper(sbProxy, "Visibility").GetAsInt(); + // If scalar bar is set to not be visible but was previously visible, + // then the user has pressed the scalar bar button hiding the scalar bar. + // We keep this information well preserved in the scalar bar representation. + // This method is not called when hiding the whole representation, so we are safe + // on scalar bar disappearance occuring on such event, it won't mess with this engine. + if (legacyVisible && !visible) + { + vtkSMPropertyHelper(sbProxy, "StickyVisible").Set(0); + } + // If the scalar bar is set to be visible, we are in the case where we automatically + // show the scalarbar, whether it was previously hidden. + else if (visible) + { + vtkSMPropertyHelper(sbProxy, "StickyVisible").Set(1); + } + } - if (sbProxy) - { - const int legacyVisible = vtkSMPropertyHelper(sbProxy, "Visibility").GetAsInt(); - // If scalar bar is set to not be visible but was previously visible, - // then the user has pressed the scalar bar button hiding the scalar bar. - // We keep this information well preserved in the scalar bar representation. - // This method is not called when hiding the whole representation, so we are safe - // on scalar bar disappearance occuring on such event, it won't mess with this engine. - if (legacyVisible && !visible) + // if hiding the Scalar Bar, just look if there's a LUT and then hide the + // corresponding scalar bar. We won't worry too much about whether scalar + // coloring is currently enabled for this. + if (!visible) { - vtkSMPropertyHelper(sbProxy, "StickyVisible").Set(0); + if (sbProxy) + { + vtkSMPropertyHelper(sbProxy, "Visibility").Set(0); + vtkSMPropertyHelper(sbProxy, "Enabled").Set(0); + sbProxy->UpdateVTKObjects(); + } + updatedRanges[i] = true; + continue; } - // If the scalar bar is set to be visible, we are in the case where we automatically - // show the scalarbar, whether it was previously hidden. - else if (visible) + + if (!blocksUsingScalarColoring[i]) { - vtkSMPropertyHelper(sbProxy, "StickyVisible").Set(1); + updatedRanges[i] = false; + continue; } - } - // if hiding the Scalar Bar, just look if there's a LUT and then hide the - // corresponding scalar bar. We won't worry too much about whether scalar - // coloring is currently enabled for this. - if (!visible) - { - if (sbProxy) + vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(blockLut, view); + if (!sbSMProxy) { - vtkSMPropertyHelper(sbProxy, "Visibility").Set(0); - vtkSMPropertyHelper(sbProxy, "Enabled").Set(0); - sbProxy->UpdateVTKObjects(); + vtkGenericWarningMacro("Failed to locate/create ScalarBar representation."); + updatedRanges[i] = false; + continue; } - return true; - } - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(repr, blockSelector)) - { - return false; - } + vtkSMPropertyHelper(sbSMProxy, "Enabled").Set(1); + vtkSMPropertyHelper(sbSMProxy, "Visibility").Set(1); - vtkNew mgr; - vtkSMProxy* sbSMProxy = mgr->GetScalarBarRepresentation(lutBlockProxy, view); - if (!sbSMProxy) - { - vtkGenericWarningMacro("Failed to locate/create ScalarBar representation."); - return false; - } + vtkSMProperty* titleProp = sbSMProxy->GetProperty("Title"); + vtkSMProperty* compProp = sbSMProxy->GetProperty("ComponentTitle"); - vtkSMPropertyHelper(sbSMProxy, "Enabled").Set(1); - vtkSMPropertyHelper(sbSMProxy, "Visibility").Set(1); + if (titleProp && compProp && titleProp->IsValueDefault() && compProp->IsValueDefault()) + { + vtkSMSettings* settings = vtkSMSettings::GetInstance(); - vtkSMProperty* titleProp = sbSMProxy->GetProperty("Title"); - vtkSMProperty* compProp = sbSMProxy->GetProperty("ComponentTitle"); + const ColorArray& blockAttributeTypeAndName = blockColorArrayNames[i]; + const std::string arrayName = blockAttributeTypeAndName.second; - if (titleProp && compProp && titleProp->IsValueDefault() && compProp->IsValueDefault()) - { - vtkSMSettings* settings = vtkSMSettings::GetInstance(); + // Look up array-specific title for scalar bar + std::ostringstream prefix; + prefix << ".array_lookup_tables" + << "." << arrayName << ".Title"; - const auto blockAttributeTypeAndName = - vtkSMColorMapEditorHelper::GetBlockColorArray(repr, blockSelector); - const std::string arrayName = blockAttributeTypeAndName.second; + const std::string arrayTitle = settings->GetSettingAsString(prefix.str().c_str(), arrayName); - // Look up array-specific title for scalar bar - std::ostringstream prefix; - prefix << ".array_lookup_tables" - << "." << arrayName << ".Title"; + vtkSMPropertyHelper titlePropHelper(titleProp); - const std::string arrayTitle = settings->GetSettingAsString(prefix.str().c_str(), arrayName); + if (sbProxy && strcmp(titlePropHelper.GetAsString(), arrayTitle.c_str()) != 0) + { + sbProxy->ClearRange(); + } - vtkSMPropertyHelper titlePropHelper(titleProp); + titlePropHelper.Set(arrayTitle.c_str()); - if (sbProxy && strcmp(titlePropHelper.GetAsString(), arrayTitle.c_str()) != 0) - { - sbProxy->ClearRange(); + // now, determine a name for it if possible. + vtkPVArrayInformation* blockArrayInfo = + vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray(repr, blockSelector); + vtkSMScalarBarWidgetRepresentationProxy::UpdateComponentTitle(sbSMProxy, blockArrayInfo); } + vtkSMScalarBarWidgetRepresentationProxy::PlaceInView(sbSMProxy, view); + sbSMProxy->UpdateVTKObjects(); + updatedRanges[i] = true; + } + return updatedRanges; +} - titlePropHelper.Set(arrayTitle.c_str()); - - // now, determine a name for it if possible. - auto blockArrayInfo = - vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray(repr, blockSelector); - vtkSMScalarBarWidgetRepresentationProxy::UpdateComponentTitle(sbSMProxy, blockArrayInfo); +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::SetSelectedScalarBarVisibility( + vtkSMProxy* proxy, vtkSMProxy* view, bool visible) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) + { + return vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility( + proxy, view, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy), visible); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::SetScalarBarVisibility(proxy, view, visible) }; } - vtkSMScalarBarWidgetRepresentationProxy::PlaceInView(sbSMProxy, view); - sbSMProxy->UpdateVTKObjects(); - return true; } //---------------------------------------------------------------------------- @@ -1893,7 +3457,7 @@ bool vtkSMColorMapEditorHelper::HideScalarBarIfNotNeeded(vtkSMProxy* proxy, vtkS { auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); vtkSMProperty* lutProperty = proxy->GetProperty("LookupTable"); - if (!repr || !lutProperty) + if (!view || !repr || !lutProperty) { return false; } @@ -1908,11 +3472,41 @@ bool vtkSMColorMapEditorHelper::HideScalarBarIfNotNeeded(vtkSMProxy* proxy, vtkS .arg(proxy) .arg("HideScalarBarIfNotNeeded") .arg(view) - .arg("comment", "hide scalars not actively used"); + .arg("comment", " hide scalars not actively used"); + + vtkSMProxy* lut = lutPropertyHelper.GetAsProxy(); + vtkNew tmgr; + return tmgr->HideScalarBarIfNotNeeded(lut, view); +} + +//---------------------------------------------------------------------------- +bool vtkSMColorMapEditorHelper::HideBlocksScalarBarIfNotNeeded(vtkSMProxy* proxy, vtkSMProxy* view) +{ + auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); + auto blockLUTs = vtkSMProxyProperty::SafeDownCast(repr->GetProperty("BlockLookupTables")); + if (!view || !repr || !blockLUTs || blockLUTs->GetNumberOfProxies() == 0) + { + return false; + } + + SM_SCOPED_TRACE(CallMethod) + .arg(proxy) + .arg("HideBlocksScalarBarIfNotNeeded") + .arg(view) + .arg("comment", " hide blocks scalars not actively used"); - vtkSMProxy* lutProxy = lutPropertyHelper.GetAsProxy(); vtkNew tmgr; - return tmgr->HideScalarBarIfNotNeeded(lutProxy, view); + bool result = false; + for (unsigned int i = 0; i < blockLUTs->GetNumberOfProxies(); ++i) + { + vtkSMProxy* blockLut = blockLUTs->GetProxy(i); + if (!blockLut) + { + continue; + } + result |= tmgr->HideScalarBarIfNotNeeded(blockLut, view); + } + return result; } //---------------------------------------------------------------------------- @@ -1931,22 +3525,35 @@ bool vtkSMColorMapEditorHelper::IsScalarBarVisible(vtkSMProxy* proxy, vtkSMProxy return false; } - vtkSMProxy* lutProxy = lutPropertyHelper.GetAsProxy(); - return vtkSMTransferFunctionProxy::IsScalarBarVisible(lutProxy, view); + vtkSMProxy* lut = lutPropertyHelper.GetAsProxy(); + return vtkSMTransferFunctionProxy::IsScalarBarVisible(lut, view); } //---------------------------------------------------------------------------- -bool vtkSMColorMapEditorHelper::IsBlockScalarBarVisible( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) +std::vector vtkSMColorMapEditorHelper::IsBlocksScalarBarVisible( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors) { + if (blockSelectors.empty()) + { + return std::vector(); + } auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); - vtkSMProxy* lutProxy = vtkSMColorMapEditorHelper::GetBlockLUTProxy(proxy, view, blockSelector); - if (!repr || !lutProxy) + std::vector blockLuts = + vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, view, blockSelectors); + if (!repr) { - return false; + return std::vector(blockSelectors.size(), false); } - - return vtkSMTransferFunctionProxy::IsScalarBarVisible(lutProxy, view); + std::vector blockScalarBarVisibleVec(blockSelectors.size(), false); + for (unsigned int i = 0; i < blockSelectors.size(); ++i) + { + vtkSMProxy* blockLut = blockLuts[i]; + if (blockLut) + { + blockScalarBarVisibleVec[i] = vtkSMTransferFunctionProxy::IsScalarBarVisible(blockLut, view); + } + } + return blockScalarBarVisibleVec; } //---------------------------------------------------------------------------- @@ -2008,72 +3615,80 @@ vtkPVArrayInformation* vtkSMColorMapEditorHelper::GetArrayInformationForColorArr } //---------------------------------------------------------------------------- -vtkPVArrayInformation* vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray( - vtkSMProxy* proxy, const std::string& blockSelector, bool checkRepresentedData) +std::vector +vtkSMColorMapEditorHelper::GetBlocksArrayInformationForColorArray( + vtkSMProxy* proxy, const std::vector& blockSelectors) { - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector)) - { - return nullptr; - } - - auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); - // now, determine a name for it if possible. - const auto attributeTypeAndArrayName = - vtkSMColorMapEditorHelper::GetBlockColorArray(repr, blockSelector); - const int attributeType = attributeTypeAndArrayName.first; - const std::string arrayName = attributeTypeAndArrayName.second; - - const vtkSMPropertyHelper inputHelper(repr->GetProperty("Input")); - vtkSMSourceProxy* inputProxy = vtkSMSourceProxy::SafeDownCast(inputHelper.GetAsProxy()); - const int port = inputHelper.GetOutputPort(); - if (!inputProxy || !inputProxy->GetOutputPort(port)) + if (blockSelectors.empty()) { - vtkWarningWithObjectMacro(repr, "No input present. Cannot determine data ranges."); - return nullptr; + return std::vector(); } + const std::vector blocksUsingScalarColoring = + vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(proxy, blockSelectors); - if (inputProxy) + std::vector blockArrayInfos(blockSelectors.size(), nullptr); + for (unsigned int i = 0; i < blockSelectors.size(); ++i) { - auto blockDataInfo = - inputProxy->GetOutputPort(port)->GetSubsetDataInformation(blockSelector.c_str(), - vtkSMPropertyHelper(repr->GetProperty("Assembly"), true).GetAsString()); - - vtkPVArrayInformation* blockArrayInfo = nullptr; - blockArrayInfo = blockDataInfo->GetArrayInformation(arrayName.c_str(), attributeType); - if (blockArrayInfo) + const std::string& blockSelector = blockSelectors[i]; + if (!blocksUsingScalarColoring[i]) { - return blockArrayInfo; + blockArrayInfos[i] = nullptr; } - - if (attributeType == vtkDataObject::POINT_THEN_CELL) + else { - // Try points... - blockArrayInfo = blockDataInfo->GetArrayInformation(arrayName.c_str(), vtkDataObject::POINT); - if (blockArrayInfo) + auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); + // now, determine a name for it if possible. + const ColorArray attributeTypeAndArrayName = + vtkSMColorMapEditorHelper::GetBlockColorArray(repr, blockSelector); + const int attributeType = attributeTypeAndArrayName.first; + const std::string arrayName = attributeTypeAndArrayName.second; + + const vtkSMPropertyHelper inputHelper(repr->GetProperty("Input")); + vtkSMSourceProxy* inputProxy = vtkSMSourceProxy::SafeDownCast(inputHelper.GetAsProxy()); + const int port = inputHelper.GetOutputPort(); + if (!inputProxy || !inputProxy->GetOutputPort(port)) { - return blockArrayInfo; + vtkWarningWithObjectMacro(repr, "No input present. Cannot determine data ranges."); + blockArrayInfos[i] = nullptr; + continue; } - - // ... then cells - blockArrayInfo = blockDataInfo->GetArrayInformation(arrayName.c_str(), vtkDataObject::CELL); - if (blockArrayInfo) + else // inputProxy && inputProxy->GetOutputPort(port) { - return blockArrayInfo; - } - } - } + vtkPVDataInformation* blockDataInfo = + inputProxy->GetOutputPort(port)->GetSubsetDataInformation(blockSelector.c_str(), + vtkSMPropertyHelper(repr->GetProperty("Assembly"), true).GetAsString()); - if (checkRepresentedData) - { - vtkPVArrayInformation* arrayInfo = - repr->GetRepresentedDataInformation()->GetArrayInformation(arrayName.c_str(), attributeType); - if (arrayInfo) - { - return arrayInfo; + vtkPVArrayInformation* blockArrayInfo = nullptr; + blockArrayInfo = blockDataInfo->GetArrayInformation(arrayName.c_str(), attributeType); + if (blockArrayInfo) + { + blockArrayInfos[i] = blockArrayInfo; + } + + if (attributeType == vtkDataObject::POINT_THEN_CELL) + { + // Try points... + blockArrayInfo = + blockDataInfo->GetArrayInformation(arrayName.c_str(), vtkDataObject::POINT); + if (blockArrayInfo) + { + blockArrayInfos[i] = blockArrayInfo; + } + + // ... then cells + blockArrayInfo = + blockDataInfo->GetArrayInformation(arrayName.c_str(), vtkDataObject::CELL); + if (blockArrayInfo) + { + blockArrayInfos[i] = blockArrayInfo; + } + } + } + // In GetArrayInformationForColorArray we also check for checkRepresentedData, + // but we can not do that for blocks, so we skip it. } } - - return nullptr; + return blockArrayInfos; } //---------------------------------------------------------------------------- @@ -2102,29 +3717,40 @@ vtkSMColorMapEditorHelper::GetProminentValuesInformationForColorArray( } //---------------------------------------------------------------------------- -vtkPVProminentValuesInformation* -vtkSMColorMapEditorHelper::GetBlockProminentValuesInformationForColorArray(vtkSMProxy* proxy, - const std::string& blockSelector, double uncertaintyAllowed, double fraction, bool force) +std::vector +vtkSMColorMapEditorHelper::GetBlocksProminentValuesInformationForColorArray(vtkSMProxy* proxy, + const std::vector& blockSelectors, double uncertaintyAllowed, double fraction, + bool force) { - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector)) + if (blockSelectors.empty()) { - return nullptr; + return std::vector(); } + auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); + + const std::vector blockArrayInfos = + vtkSMColorMapEditorHelper::GetBlocksArrayInformationForColorArray(proxy, blockSelectors); + const std::vector blockColorArrays = + vtkSMColorMapEditorHelper::GetBlocksColorArrays(proxy, blockSelectors); - vtkPVArrayInformation* blockArrayInfo = - vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray(proxy, blockSelector); - if (!blockArrayInfo) + std::vector blockProminentValuesInfos( + blockSelectors.size(), nullptr); + for (unsigned int i = 0; i < blockSelectors.size(); ++i) { - return nullptr; + if (vtkPVArrayInformation* blockArrayInfo = blockArrayInfos[i]) + { + const auto attributeTypeAndArrayName = blockColorArrays[i]; + blockProminentValuesInfos[i] = repr->GetBlockProminentValuesInformation(blockSelectors[i], + vtkSMPropertyHelper(repr->GetProperty("Assembly"), true).GetAsString(), + blockArrayInfo->GetName(), attributeTypeAndArrayName.first, + blockArrayInfo->GetNumberOfComponents(), uncertaintyAllowed, fraction, force); + } + else + { + blockProminentValuesInfos[i] = nullptr; + } } - - auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); - const auto attributeTypeAndArrayName = - vtkSMColorMapEditorHelper::GetBlockColorArray(repr, blockSelector); - return repr->GetBlockProminentValuesInformation(blockSelector, - vtkSMPropertyHelper(repr->GetProperty("Assembly"), true).GetAsString(), - blockArrayInfo->GetName(), attributeTypeAndArrayName.first, - blockArrayInfo->GetNumberOfComponents(), uncertaintyAllowed, fraction, force); + return blockProminentValuesInfos; } //---------------------------------------------------------------------------- @@ -2156,9 +3782,9 @@ int vtkSMColorMapEditorHelper::GetEstimatedNumberOfAnnotationsOnScalarBar( return -1; } - vtkSMProxy* lutProxy = lutPropertyHelper.GetAsProxy(); + vtkSMProxy* lut = lutPropertyHelper.GetAsProxy(); vtkNew mgr; - vtkSMProxy* sbProxy = mgr->GetScalarBarRepresentation(lutProxy, view); + vtkSMProxy* sbProxy = mgr->GetScalarBarRepresentation(lut, view); if (!sbProxy) { vtkGenericWarningMacro("Failed to locate/create ScalarBar representation."); @@ -2170,65 +3796,124 @@ int vtkSMColorMapEditorHelper::GetEstimatedNumberOfAnnotationsOnScalarBar( } //---------------------------------------------------------------------------- -int vtkSMColorMapEditorHelper::GetBlockEstimatedNumberOfAnnotationsOnScalarBar( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) +std::vector vtkSMColorMapEditorHelper::GetBlocksEstimatedNumberOfAnnotationsOnScalarBars( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors) { - if (!vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(proxy, blockSelector)) + if (blockSelectors.empty()) { - return 0; + return std::vector(); + } + auto repr = vtkSMRepresentationProxy::SafeDownCast(proxy); + if (!repr || !view) + { + return std::vector(blockSelectors.size(), -1); } + const std::vector blocksUsingScalarColoring = + vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(proxy, blockSelectors); + const std::vector blockLuts = + vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, blockSelectors); - vtkSMProxy* blockLutProxy = - vtkSMColorMapEditorHelper::GetBlockLUTProxy(proxy, view, blockSelector); - if (!blockLutProxy) + std::vector estimatedNumberOfAnnotationsVec(blockSelectors.size()); + for (unsigned int i = 0; i < blockSelectors.size(); ++i) { - return -1; + if (!blocksUsingScalarColoring[i]) + { + estimatedNumberOfAnnotationsVec[i] = 0; + } + else + { + if (vtkSMProxy* blockLut = blockLuts[i]) + { + vtkNew mgr; + vtkSMProxy* sbProxy = mgr->GetScalarBarRepresentation(blockLut, view); + if (!sbProxy) + { + vtkGenericWarningMacro("Failed to locate/create ScalarBar representation."); + estimatedNumberOfAnnotationsVec[i] = -1; + } + else + { + sbProxy->UpdatePropertyInformation(); + estimatedNumberOfAnnotationsVec[i] = + vtkSMPropertyHelper(sbProxy, "EstimatedNumberOfAnnotations").GetAsInt(); + } + } + else + { + estimatedNumberOfAnnotationsVec[i] = -1; + } + } } + return estimatedNumberOfAnnotationsVec; +} - vtkNew mgr; - vtkSMProxy* sbProxy = mgr->GetScalarBarRepresentation(blockLutProxy, view); - if (!sbProxy) +//---------------------------------------------------------------------------- +std::vector vtkSMColorMapEditorHelper::GetSelectedEstimatedNumberOfAnnotationsOnScalarBars( + vtkSMProxy* proxy, vtkSMProxy* view) +{ + if (this->SelectedPropertiesType == SelectedPropertiesTypes::Blocks) { - vtkGenericWarningMacro("Failed to locate/create ScalarBar representation."); - return -1; + return vtkSMColorMapEditorHelper::GetBlocksEstimatedNumberOfAnnotationsOnScalarBars( + proxy, view, vtkSMColorMapEditorHelper::GetSelectedBlockSelectors(proxy)); + } + else // this->SelectedPropertiesType == SelectedPropertiesTypes::Representation + { + return { vtkSMColorMapEditorHelper::GetEstimatedNumberOfAnnotationsOnScalarBar(proxy, view) }; } +} - sbProxy->UpdatePropertyInformation(); - return vtkSMPropertyHelper(sbProxy, "EstimatedNumberOfAnnotations").GetAsInt(); +//---------------------------------------------------------------------------- +int vtkSMColorMapEditorHelper::GetAnySelectedEstimatedNumberOfAnnotationsOnScalarBar( + vtkSMProxy* proxy, vtkSMProxy* view) +{ + const std::vector annotations = + vtkSMColorMapEditorHelper::GetSelectedEstimatedNumberOfAnnotationsOnScalarBars(proxy, view); + auto findResult = std::find_if( + annotations.begin(), annotations.end(), [](int annotation) { return annotation >= 0; }); + return findResult != annotations.end() ? *findResult : -1; } //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMColorMapEditorHelper::GetLastLUTProxy(vtkSMProxy* proxy) +vtkSMProxy* vtkSMColorMapEditorHelper::GetLastLookupTable(vtkSMProxy* proxy) { auto repr = vtkSMPVRepresentationProxy::SafeDownCast(proxy); - return repr ? repr->GetLastLUTProxy() : nullptr; + return repr ? repr->GetLastLookupTable() : nullptr; } //---------------------------------------------------------------------------- -void vtkSMColorMapEditorHelper::SetLastLUTProxy(vtkSMProxy* proxy, vtkSMProxy* lutProxy) +void vtkSMColorMapEditorHelper::SetLastLookupTable(vtkSMProxy* proxy, vtkSMProxy* lut) { auto repr = vtkSMPVRepresentationProxy::SafeDownCast(proxy); if (repr) { - repr->SetLastLUTProxy(lutProxy); + repr->SetLastLookupTable(lut); } } //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMColorMapEditorHelper::GetLastBlockLUTProxy( - vtkSMProxy* proxy, const std::string& blockSelector) +std::vector vtkSMColorMapEditorHelper::GetLastBlocksLookupTables( + vtkSMProxy* proxy, const std::vector& blockSelectors) { + if (blockSelectors.empty()) + { + return std::vector(); + } auto repr = vtkSMPVRepresentationProxy::SafeDownCast(proxy); - return repr ? repr->GetLastBlockLUTProxy(blockSelector) : nullptr; + return repr ? repr->GetLastBlocksLookupTables(blockSelectors) + : std::vector(blockSelectors.size(), nullptr); } //---------------------------------------------------------------------------- -void vtkSMColorMapEditorHelper::SetLastBlockLUTProxy( - vtkSMProxy* proxy, const std::string& blockSelector, vtkSMProxy* lutProxy) +void vtkSMColorMapEditorHelper::SetLastBlocksLookupTable( + vtkSMProxy* proxy, const std::vector& blockSelectors, vtkSMProxy* lut) { + if (blockSelectors.empty()) + { + return; + } auto repr = vtkSMPVRepresentationProxy::SafeDownCast(proxy); if (repr) { - repr->SetLastBlockLUTProxy(lutProxy, blockSelector); + repr->SetLastBlocksLookupTable(blockSelectors, lut); } } diff --git a/Remoting/Views/vtkSMColorMapEditorHelper.h b/Remoting/Views/vtkSMColorMapEditorHelper.h index 51bbe31855a..6890e94a7c3 100644 --- a/Remoting/Views/vtkSMColorMapEditorHelper.h +++ b/Remoting/Views/vtkSMColorMapEditorHelper.h @@ -12,25 +12,150 @@ #ifndef vtkSMColorMapEditorHelper_h #define vtkSMColorMapEditorHelper_h -#include "vtkRemotingViewsModule.h" //needed for exports +#include "vtkObject.h" // Superclass +#include "vtkParaViewDeprecation.h" // For PARAVIEW_DEPRECATED_IN_5_13_0 +#include "vtkRemotingViewsModule.h" // needed for exports #include "vtkSmartPointer.h" // For LastLUTProxy +#include // For array +#include // For int +#include // For map +#include // For string +#include // For vector + class vtkPVArrayInformation; class vtkPVProminentValuesInformation; +class vtkSMProperty; class vtkSMProxy; class vtkSMPVRepresentationProxy; -class VTKREMOTINGVIEWS_EXPORT vtkSMColorMapEditorHelper +// template +class VTKREMOTINGVIEWS_EXPORT vtkSMColorMapEditorHelper : public vtkObject { public: + static vtkSMColorMapEditorHelper* New(); + vtkTypeMacro(vtkSMColorMapEditorHelper, vtkObject); + void PrintSelf(ostream& os, vtkIndent indent) override; + + enum SelectedPropertiesTypes + { + Representation = 0, + Blocks = 1 + }; + + enum BlockPropertyState : std::uint8_t + { + // Property is disabled because it can not be edited + Disabled = 0x0, + // Property is inherited from the representation + RepresentationInherited = 0x1, + // Property is inherited from a block + BlockInherited = 0x2, + // Property is inherited from a block and the representation + MixedInherited = 0x3, + // Property is set in all blocks + Set = 0x4, + // Property is set in some blocks and representation inherited + SetAndRepresentationInherited = 0x5, + // Property is set in some blocks and block inherited + SetAndBlockInherited = 0x6, + // Property is set in some blocks and mixed inherited + SetAndMixedInherited = 0x7, + // Number of possible states + NumberOfStates = 0x8 + }; + + ///@{ + /** + * Set/Get the selected properties type. + * + * This variable is used in all instance functions with the Selected key-word in the name. + * + * It decides whether to use a function that uses the representation's or blocks' properties. + * + * Default is false. + */ + vtkSetClampMacro(SelectedPropertiesType, int, SelectedPropertiesTypes::Representation, + SelectedPropertiesTypes::Blocks); + void SetSelectedPropertiesTypeToRepresentation() + { + this->SetSelectedPropertiesType(SelectedPropertiesTypes::Representation); + } + void SetSelectedPropertiesTypeToBlocks() + { + this->SetSelectedPropertiesType(SelectedPropertiesTypes::Blocks); + } + vtkGetMacro(SelectedPropertiesType, int); + ///@} + + /** + * Determine if a property is a block property. + */ + static SelectedPropertiesTypes GetPropertyType(vtkSMProperty* property); + + ///@{ + /** + * Determine if a propert(y/ies) of a selector(s) is set, inherited from a block, or from the + * representation, or mixed. + * + * if it's RepresentationInherited, the blockSelector will be empty. + * if it's BlockInherited, the blockSelector will be the inherited block. + * if it's MixedInherited, the blockSelector will be the inherited block. + * if it's Set, the block selector will be the block where the property is set. + * if it's SetAndRepresentationInherited, be the block where the property is set. + * if it's SetAndBlockInherited, be the block where the property is set. + * if it's SetAndMixedInherited, be the block where the property is set. + */ + static std::pair HasBlockProperty( + vtkSMProxy* proxy, const std::string& blockSelector, const std::string& propertyName); + static std::pair GetBlockPropertyStateFromBlockPropertyStates( + const std::vector>& states); + static std::pair HasBlockProperty(vtkSMProxy* proxy, + const std::vector& blockSelectors, const std::string& propertyName); + static std::pair HasBlockProperties(vtkSMProxy* proxy, + const std::string& blockSelector, const std::vector& propertyNames); + static std::pair HasBlocksProperties(vtkSMProxy* proxy, + const std::vector& blockSelectors, const std::vector& propertyNames); + ///@} + + /** + * Returns the selected block selectors for this representation. + */ + static std::vector GetSelectedBlockSelectors(vtkSMProxy* proxy); + + /** + * Returns the selectors that use a color array for this representation. + */ + static std::vector GetColorArraysBlockSelectors(vtkSMProxy* proxy); + ///@{ /** * Returns the lut proxy of this representation in the given view. - * This method will return `nullptr` if no lut proxy exists in this view. + * + * The methods with a `view` parameter will return `nullptr` if the view is not a render view. */ - static vtkSMProxy* GetLUTProxy(vtkSMProxy* proxy, vtkSMProxy* view); - static vtkSMProxy* GetBlockLUTProxy( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector); + PARAVIEW_DEPRECATED_IN_5_13_0("Use GetLookupTable instead") + static vtkSMProxy* GetLUTProxy(vtkSMProxy* proxy, vtkSMProxy* view) + { + return vtkSMColorMapEditorHelper::GetLookupTable(proxy, view); + } + static vtkSMProxy* GetLookupTable(vtkSMProxy* proxy); + static vtkSMProxy* GetLookupTable(vtkSMProxy* proxy, vtkSMProxy* view); + static vtkSMProxy* GetBlockLookupTable(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, { blockSelector }).front(); + } + static std::vector GetBlocksLookupTables( + vtkSMProxy* proxy, const std::vector& blockSelectors); + static vtkSMProxy* GetBlockLookupTable( + vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksLookupTables(proxy, view, { blockSelector }).front(); + } + static std::vector GetBlocksLookupTables( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors); + std::vector GetSelectedLookupTables(vtkSMProxy* proxy); + std::vector GetSelectedLookupTables(vtkSMProxy* proxy, vtkSMProxy* view); ///@} ///@{ @@ -40,8 +165,16 @@ public: * check for the validity of the array. */ static bool GetUsingScalarColoring(vtkSMProxy* proxy); - static bool GetBlockUsingScalarColoring(vtkSMProxy* proxy, const std::string& blockSelector); + static bool GetBlockUsingScalarColoring(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(proxy, { blockSelector }) + .front(); + } + static std::vector GetBlocksUsingScalarColoring( + vtkSMProxy* proxy, const std::vector& blockSelectors); static bool GetAnyBlockUsingScalarColoring(vtkSMProxy* proxy); + std::vector GetSelectedUsingScalarColorings(vtkSMProxy* proxy); + bool GetAnySelectedUsingScalarColoring(vtkSMProxy* proxy); ///@} ///@{ @@ -50,7 +183,7 @@ public: * representation if a scalar bar is being used for `proxy`. */ static void SetupLookupTable(vtkSMProxy* proxy); - static void SetupBlockLookupTable(vtkSMProxy* proxy, const std::string& blockSelector); + static void SetupBlocksLookupTables(vtkSMProxy* proxy); ///@} ///@{ @@ -62,8 +195,8 @@ public: * with the new range value. */ static bool UpdateScalarBarRange(vtkSMProxy* proxy, vtkSMProxy* view, bool deleteRange); - static bool UpdateBlockScalarBarRange( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector, bool deleteRange); + static std::vector UpdateBlocksScalarBarRange( + vtkSMProxy* proxy, vtkSMProxy* view, bool deleteRange); ///@} ///@{ @@ -75,7 +208,16 @@ public: */ static bool SetScalarColoring(vtkSMProxy* proxy, const char* arrayName, int attributeType); static bool SetBlockScalarColoring( - vtkSMProxy* proxy, const std::string& blockSelector, const char* arrayName, int attributeType); + vtkSMProxy* proxy, const std::string& blockSelector, const char* arrayName, int attributeType) + { + return vtkSMColorMapEditorHelper::SetBlocksScalarColoring( + proxy, { blockSelector }, arrayName, attributeType) + .front(); + } + static std::vector SetBlocksScalarColoring(vtkSMProxy* proxy, + const std::vector& blockSelectors, const char* arrayName, int attributeType); + std::vector SetSelectedScalarColoring( + vtkSMProxy* proxy, const char* arrayName, int attributeType); ///@} ///@{ @@ -90,7 +232,17 @@ public: static bool SetScalarColoring( vtkSMProxy* proxy, const char* arrayName, int attributeType, int component); static bool SetBlockScalarColoring(vtkSMProxy* proxy, const std::string& blockSelector, - const char* arrayName, int attributeType, int component); + const char* arrayName, int attributeType, int component) + { + return vtkSMColorMapEditorHelper::SetBlocksScalarColoring( + proxy, { blockSelector }, arrayName, attributeType, component) + .front(); + } + static std::vector SetBlocksScalarColoring(vtkSMProxy* proxy, + const std::vector& blockSelectors, const char* arrayName, int attributeType, + int component); + std::vector SetSelectedScalarColoring( + vtkSMProxy* proxy, const char* arrayName, int attributeType, int component); ///@} ///@{ @@ -105,7 +257,16 @@ public: static bool RescaleTransferFunctionToDataRange( vtkSMProxy* proxy, bool extend = false, bool force = true); static bool RescaleBlockTransferFunctionToDataRange( - vtkSMProxy* proxy, const std::string& blockSelector, bool extend = false, bool force = true); + vtkSMProxy* proxy, const std::string& blockSelector, bool extend = false, bool force = true) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + proxy, { blockSelector }, extend, force) + .front(); + } + static std::vector RescaleBlocksTransferFunctionToDataRange(vtkSMProxy* proxy, + const std::vector& blockSelectors, bool extend = false, bool force = true); + std::vector RescaleSelectedTransferFunctionToDataRange( + vtkSMProxy* proxy, bool extend = false, bool force = true); ///@} ///@{ @@ -123,7 +284,17 @@ public: int attributeType, bool extend = false, bool force = true); static bool RescaleBlockTransferFunctionToDataRange(vtkSMProxy* proxy, const std::string& blockSelector, const char* arrayName, int attributeType, bool extend = false, - bool force = true); + bool force = true) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + proxy, { blockSelector }, arrayName, attributeType, extend, force) + .front(); + } + static std::vector RescaleBlocksTransferFunctionToDataRange(vtkSMProxy* proxy, + const std::vector& blockSelectors, const char* arrayName, int attributeType, + bool extend = false, bool force = true); + std::vector RescaleSelectedTransferFunctionToDataRange(vtkSMProxy* proxy, + const char* arrayName, int attributeType, bool extend = false, bool force = true); ///@} ///@{ @@ -133,7 +304,15 @@ public: */ static bool RescaleTransferFunctionToDataRangeOverTime(vtkSMProxy* proxy); static bool RescaleBlockTransferFunctionToDataRangeOverTime( - vtkSMProxy* proxy, const std::string& blockSelector); + vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + proxy, { blockSelector }) + .front(); + } + static std::vector RescaleBlocksTransferFunctionToDataRangeOverTime( + vtkSMProxy* proxy, const std::vector& blockSelectors); + std::vector RescaleSelectedTransferFunctionToDataRangeOverTime(vtkSMProxy* proxy); ///@} ///@{ @@ -146,7 +325,17 @@ public: static bool RescaleTransferFunctionToDataRangeOverTime( vtkSMProxy* proxy, const char* arrayName, int attributeType); static bool RescaleBlockTransferFunctionToDataRangeOverTime( - vtkSMProxy* proxy, const std::string& blockSelector, const char* arrayName, int attributeType); + vtkSMProxy* proxy, const std::string& blockSelector, const char* arrayName, int attributeType) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + proxy, { blockSelector }, arrayName, attributeType) + .front(); + } + static std::vector RescaleBlocksTransferFunctionToDataRangeOverTime( + vtkSMProxy* proxy, const std::vector& blockSelectors, const char* arrayName, + int attributeType); + std::vector RescaleSelectedTransferFunctionToDataRangeOverTime( + vtkSMProxy* proxy, const char* arrayName, int attributeType); ///@} ///@{ @@ -157,10 +346,6 @@ public: static bool RescaleTransferFunctionToVisibleRange(vtkSMProxy* proxy, vtkSMProxy* view); static bool RescaleTransferFunctionToVisibleRange( vtkSMProxy* proxy, vtkSMProxy* view, const char* arrayName, int attributeType); - static bool RescaleBlockTransferFunctionToVisibleRange( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector); - static bool RescaleBlockTransferFunctionToVisibleRange(vtkSMProxy* proxy, vtkSMProxy* view, - const std::string& blockSelector, const char* arrayName, int attributeType); ///@} ///@{ @@ -170,9 +355,19 @@ public: */ static bool SetScalarBarVisibility(vtkSMProxy* proxy, vtkSMProxy* view, bool visible); static bool SetBlockScalarBarVisibility( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector, bool visible); + vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector, bool visible) + { + return vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility( + proxy, view, { blockSelector }, visible) + .front(); + } + static std::vector SetBlocksScalarBarVisibility(vtkSMProxy* proxy, vtkSMProxy* view, + const std::vector& blockSelectors, bool visible); + std::vector SetSelectedScalarBarVisibility( + vtkSMProxy* proxy, vtkSMProxy* view, bool visible); ///@} + ///@{ /** * While SetScalarBarVisibility can be used to hide a scalar bar, it will * always simply hide the scalar bar even if its being used by some other @@ -181,6 +376,8 @@ public: * scalar bar. */ static bool HideScalarBarIfNotNeeded(vtkSMProxy* repr, vtkSMProxy* view); + static bool HideBlocksScalarBarIfNotNeeded(vtkSMProxy* repr, vtkSMProxy* view); + ///@} ///@{ /** @@ -189,7 +386,13 @@ public: */ static bool IsScalarBarVisible(vtkSMProxy* repr, vtkSMProxy* view); static bool IsBlockScalarBarVisible( - vtkSMProxy* repr, vtkSMProxy* view, const std::string& blockSelector); + vtkSMProxy* repr, vtkSMProxy* view, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::IsBlocksScalarBarVisible(repr, view, { blockSelector }) + .front(); + } + static std::vector IsBlocksScalarBarVisible( + vtkSMProxy* repr, vtkSMProxy* view, const std::vector& blockSelectors); ///@} ///@{ @@ -201,7 +404,14 @@ public: static vtkPVArrayInformation* GetArrayInformationForColorArray( vtkSMProxy* proxy, bool checkRepresentedData = true); static vtkPVArrayInformation* GetBlockArrayInformationForColorArray( - vtkSMProxy* proxy, const std::string& blockSelector, bool checkRepresentedData = true); + vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksArrayInformationForColorArray( + proxy, { blockSelector }) + .front(); + } + static std::vector GetBlocksArrayInformationForColorArray( + vtkSMProxy* proxy, const std::vector& blockSelectors); ///@} ///@{ @@ -211,7 +421,14 @@ public: */ static std::string GetDecoratedArrayName(vtkSMProxy* proxy, const std::string& arrayName); static std::string GetBlockDecoratedArrayName( - vtkSMProxy* proxy, const std::string& blockSelector, const std::string& arrayName); + vtkSMProxy* proxy, const std::string& blockSelector, const std::string& arrayName) + { + return vtkSMColorMapEditorHelper::GetBlocksDecoratedArrayNames( + proxy, { blockSelector }, arrayName) + .front(); + } + static std::vector GetBlocksDecoratedArrayNames(vtkSMProxy* proxy, + const std::vector& blockSelectors, const std::string& arrayName); ///@} ///@{ @@ -224,16 +441,37 @@ public: bool force = false); static vtkPVProminentValuesInformation* GetBlockProminentValuesInformationForColorArray( vtkSMProxy* proxy, const std::string& blockSelector, double uncertaintyAllowed = 1e-6, + double fraction = 1e-3, bool force = false) + { + return vtkSMColorMapEditorHelper::GetBlocksProminentValuesInformationForColorArray( + proxy, { blockSelector }, uncertaintyAllowed, fraction, force) + .front(); + } + static std::vector + GetBlocksProminentValuesInformationForColorArray(vtkSMProxy* proxy, + const std::vector& blockSelectors, double uncertaintyAllowed = 1e-6, double fraction = 1e-3, bool force = false); ///@} ///@{ /** * Get an estimated number of annotation shown on this representation scalar bar + * + * If the return value is -1, then the value is not set. */ static int GetEstimatedNumberOfAnnotationsOnScalarBar(vtkSMProxy* proxy, vtkSMProxy* view); static int GetBlockEstimatedNumberOfAnnotationsOnScalarBar( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector); + vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksEstimatedNumberOfAnnotationsOnScalarBars( + proxy, view, { blockSelector }) + .front(); + } + static std::vector GetBlocksEstimatedNumberOfAnnotationsOnScalarBars( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors); + std::vector GetSelectedEstimatedNumberOfAnnotationsOnScalarBars( + vtkSMProxy* proxy, vtkSMProxy* view); + int GetAnySelectedEstimatedNumberOfAnnotationsOnScalarBar(vtkSMProxy* proxy, vtkSMProxy* view); ///@} ///@{ @@ -243,21 +481,70 @@ public: * is also visible. * It returns 1 if the scalar bar is sticky visible, 0 other wise. * If any problem is encountered, for example if view == nullptr, - * or if the scalar bar representation is not instanciated / found, + * or if the scalar bar representation is not instantiated / found, * it returns -1. */ static int IsScalarBarStickyVisible(vtkSMProxy* proxy, vtkSMProxy* view); static int IsBlockScalarBarStickyVisible( - vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector); + vtkSMProxy* proxy, vtkSMProxy* view, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::IsBlocksScalarBarStickyVisible(proxy, view, { blockSelector }) + .front(); + } + static std::vector IsBlocksScalarBarStickyVisible( + vtkSMProxy* proxy, vtkSMProxy* view, const std::vector& blockSelectors); + ///@} + using Color = std::array; ///@{ /** - * Set/Get the block color array name. + * Set/Get the color of the representation. + * + * @note Use `IsColorValid` to check if the returned value is valid/set. */ - static void SetBlockColorArray( - vtkSMProxy* proxy, const std::string& blockSelector, int attributeType, std::string arrayName); - static std::pair GetBlockColorArray( - vtkSMProxy* proxy, const std::string& blockSelector); + static bool IsColorValid(Color color); + static void SetColor(vtkSMProxy* proxy, Color color); + static void SetBlockColor(vtkSMProxy* proxy, const std::string& blockSelector, Color color) + { + vtkSMColorMapEditorHelper::SetBlocksColor(proxy, { blockSelector }, color); + } + static void SetBlocksColor( + vtkSMProxy* proxy, const std::vector& blockSelectors, Color color); + static void RemoveBlockColor(vtkSMProxy* proxy, const std::string& blockSelector) + { + vtkSMColorMapEditorHelper::RemoveBlocksColors(proxy, { blockSelector }); + } + static void RemoveBlocksColors(vtkSMProxy* proxy, const std::vector& blockSelectors); + void SetSelectedColor(vtkSMProxy* proxy, Color color); + static Color GetColor(vtkSMProxy* proxy); + static Color GetBlockColor(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksColors(proxy, { blockSelector }).front(); + } + static std::vector GetBlocksColors( + vtkSMProxy* proxy, const std::vector& blockSelectors); + std::vector GetSelectedColors(vtkSMProxy* proxy); + ///@} + + ///@{ + /** + * Get Color array property + */ + static vtkSMProperty* GetColorArrayProperty(vtkSMProxy* proxy); + static vtkSMProperty* GetBlockColorArrayProperty(vtkSMProxy* proxy); + vtkSMProperty* GetSelectedColorArrayProperty(vtkSMProxy* proxy); + ///@} + + using ColorArray = std::pair; + ///@{ + /** + * Get the color array name. + * + * @note Use `IsColorArrayValid` to check if the returned value is valid/set. + */ + static bool IsColorArrayValid(const ColorArray& array); + static std::vector GetBlocksColorArrays( + vtkSMProxy* proxy, const std::vector& blockSelectors); static int GetBlockColorArrayAssociation(vtkSMProxy* proxy, const std::string& blockSelector) { return vtkSMColorMapEditorHelper::GetBlockColorArray(proxy, blockSelector).first; @@ -266,18 +553,184 @@ public: { return vtkSMColorMapEditorHelper::GetBlockColorArray(proxy, blockSelector).second; } + static std::map> GetCommonColorArraysBlockSelectors( + vtkSMProxy* proxy, const std::vector& blockSelectors); + std::vector GetSelectedColorArrays(vtkSMProxy* proxy); ///@} ///@{ /** - * Set/Get if we should use a separate color map for this block. + * Get use separate color map property */ + static vtkSMProperty* GetUseSeparateColorMapProperty(vtkSMProxy* proxy); + static vtkSMProperty* GetBlockUseSeparateColorMapProperty(vtkSMProxy* proxy); + vtkSMProperty* GetSelectedUseSeparateColorMapProperty(vtkSMProxy* proxy); + ///@} + + ///@{ + /** + * Set/Get if we should use a separate color map. + * + * @note Use `IsUseSeparateColorMapValid` to check if the returned value is valid/set. + */ + static bool IsUseSeparateColorMapValid(int useSeparateColorMap); + static void SetUseSeparateColorMap(vtkSMProxy* proxy, bool use); static void SetBlockUseSeparateColorMap( - vtkSMProxy* proxy, const std::string& blockSelector, bool use); - static bool GetBlockUseSeparateColorMap(vtkSMProxy* proxy, const std::string& blockSelector); + vtkSMProxy* proxy, const std::string& blockSelector, bool use) + { + vtkSMColorMapEditorHelper::SetBlocksUseSeparateColorMap(proxy, { blockSelector }, use); + } + static void SetBlocksUseSeparateColorMap( + vtkSMProxy* proxy, const std::vector& blockSelectors, bool use); + static void RemoveBlockUseSeparateColorMap(vtkSMProxy* proxy, const std::string& blockSelector) + { + vtkSMColorMapEditorHelper::RemoveBlocksUseSeparateColorMaps(proxy, { blockSelector }); + } + static void RemoveBlocksUseSeparateColorMaps( + vtkSMProxy* proxy, const std::vector& blockSelectors); + void SetSelectedUseSeparateColorMap(vtkSMProxy* proxy, bool use); + static bool GetUseSeparateColorMap(vtkSMProxy* proxy); + static int GetBlockUseSeparateColorMap(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksUseSeparateColorMaps(proxy, { blockSelector }) + .front(); + } + static std::vector GetBlocksUseSeparateColorMaps( + vtkSMProxy* proxy, const std::vector& blockSelectors); + std::vector GetSelectedUseSeparateColorMaps(vtkSMProxy* proxy); + bool GetAnySelectedUseSeparateColorMap(vtkSMProxy* proxy); + ///@} + + ///@{ + /** + * Set/Get the map scalars of the representation. + * + * @note Use `IsMapScalarsValid` to check if the returned value is valid/set. + */ + static bool IsMapScalarsValid(int mapScalars); + static void SetMapScalars(vtkSMProxy* proxy, bool mapScalars); + static void SetBlockMapScalars( + vtkSMProxy* proxy, const std::string& blockSelector, bool mapScalars) + { + vtkSMColorMapEditorHelper::SetBlocksMapScalars(proxy, { blockSelector }, mapScalars); + } + static void SetBlocksMapScalars( + vtkSMProxy* proxy, const std::vector& blockSelectors, bool mapScalars); + static void RemoveBlockMapScalars(vtkSMProxy* proxy, const std::string& blockSelector) + { + vtkSMColorMapEditorHelper::RemoveBlocksMapScalars(proxy, { blockSelector }); + } + static void RemoveBlocksMapScalars( + vtkSMProxy* proxy, const std::vector& blockSelectors); + void SetSelectedMapScalars(vtkSMProxy* proxy, bool mapScalars); + static bool GetMapScalars(vtkSMProxy* proxy); + static int GetBlockMapScalars(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksMapScalars(proxy, { blockSelector }).front(); + } + static std::vector GetBlocksMapScalars( + vtkSMProxy* proxy, const std::vector& blockSelectors); + std::vector GetSelectedMapScalars(vtkSMProxy* proxy); + bool GetAnySelectedMapScalars(vtkSMProxy* proxy); + ///@} + + ///@{ + /** + * Set/Get the map scalars of the representation. + * + * @note Use `IsInterpolateScalarsBeforeMappingValid` to check if the returned value is valid/set. + */ + static bool IsInterpolateScalarsBeforeMappingValid(int interpolate); + static void SetInterpolateScalarsBeforeMapping(vtkSMProxy* proxy, bool interpolate); + static void SetBlockInterpolateScalarsBeforeMapping( + vtkSMProxy* proxy, const std::string& blockSelector, bool interpolate) + { + return vtkSMColorMapEditorHelper::SetBlocksInterpolateScalarsBeforeMapping( + proxy, { blockSelector }, interpolate); + } + static void SetBlocksInterpolateScalarsBeforeMapping( + vtkSMProxy* proxy, const std::vector& blockSelectors, bool interpolate); + static void RemoveBlockInterpolateScalarsBeforeMapping( + vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::RemoveBlocksInterpolateScalarsBeforeMappings( + proxy, { blockSelector }); + } + static void RemoveBlocksInterpolateScalarsBeforeMappings( + vtkSMProxy* proxy, const std::vector& blockSelectors); + void SetSelectedInterpolateScalarsBeforeMapping(vtkSMProxy* proxy, bool interpolate); + static bool GetInterpolateScalarsBeforeMapping(vtkSMProxy* proxy); + static int GetBlockInterpolateScalarsBeforeMapping( + vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksInterpolateScalarsBeforeMappings( + proxy, { blockSelector }) + .front(); + } + static std::vector GetBlocksInterpolateScalarsBeforeMappings( + vtkSMProxy* proxy, const std::vector& blockSelectors); + std::vector GetSelectedInterpolateScalarsBeforeMappings(vtkSMProxy* proxy); + bool GetAnySelectedInterpolateScalarsBeforeMapping(vtkSMProxy* proxy); + ///@} + + ///@{ + /** + * Set/Get the opacity of the representation. + * + * @note Use `IsOpacityValid` to check if the returned value is valid/set. + */ + static bool IsOpacityValid(double opacity); + static void SetOpacity(vtkSMProxy* proxy, double opacity); + static void SetBlockOpacity(vtkSMProxy* proxy, const std::string& blockSelector, double opacity) + { + return vtkSMColorMapEditorHelper::SetBlocksOpacity(proxy, { blockSelector }, opacity); + } + static void SetBlocksOpacity( + vtkSMProxy* proxy, const std::vector& blockSelectors, double opacity); + static void RemoveBlockOpacity(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::RemoveBlocksOpacities(proxy, { blockSelector }); + } + static void RemoveBlocksOpacities( + vtkSMProxy* proxy, const std::vector& blockSelectors); + void SetSelectedOpacity(vtkSMProxy* proxy, double opacity); + static double GetOpacity(vtkSMProxy* proxy); + static double GetBlockOpacity(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksOpacities(proxy, { blockSelector }).front(); + } + static std::vector GetBlocksOpacities( + vtkSMProxy* proxy, const std::vector& blockSelectors); + std::vector GetSelectedOpacities(vtkSMProxy* proxy); + ///@} + + ///@{ + /** + * Reset a block property. + */ + static void ResetBlockProperty( + vtkSMProxy* proxy, const std::string& blockSelector, const std::string& propertyName) + { + vtkSMColorMapEditorHelper::ResetBlocksProperty(proxy, { blockSelector }, propertyName); + } + static void RemoveBlockProperties(vtkSMProxy* proxy, const std::string& blockSelector, + const std::vector& propertyNames) + { + vtkSMColorMapEditorHelper::ResetBlocksProperties(proxy, { blockSelector }, propertyNames); + } + static void ResetBlocksProperty(vtkSMProxy* proxy, const std::vector& blockSelectors, + const std::string& propertyName) + { + vtkSMColorMapEditorHelper::ResetBlocksProperties(proxy, blockSelectors, { propertyName }); + } + static void ResetBlocksProperties(vtkSMProxy* proxy, + const std::vector& blockSelectors, const std::vector& propertyNames); ///@} protected: + vtkSMColorMapEditorHelper(); + ~vtkSMColorMapEditorHelper() override; + ///@{ /** * Rescales transfer function ranges using the array information provided. @@ -287,25 +740,72 @@ protected: vtkSMProxy* proxy, vtkPVArrayInformation* info, bool extend = false, bool force = true); static bool RescaleBlockTransferFunctionToDataRange(vtkSMProxy* proxy, const std::string& blockSelector, vtkPVArrayInformation* info, bool extend = false, - bool force = true); + bool force = true) + { + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + proxy, { blockSelector }, { info }, extend, force) + .front(); + } + static std::vector RescaleBlocksTransferFunctionToDataRange(vtkSMProxy* proxy, + const std::vector& blockSelectors, std::vector infos, + bool extend = false, bool force = true); + ///@} + + ///@{ + /** + * Set the color array name. + */ + static void SetColorArray(vtkSMProxy* proxy, int attributeType, std::string arrayName); + static void SetBlockColorArray( + vtkSMProxy* proxy, const std::string& blockSelector, int attributeType, std::string arrayName) + { + vtkSMColorMapEditorHelper::SetBlocksColorArray( + proxy, { blockSelector }, attributeType, arrayName); + } + static void SetBlocksColorArray(vtkSMProxy* proxy, const std::vector& blockSelectors, + int attributeType, std::string arrayName); + static void RemoveBlockColorArray(vtkSMProxy* proxy, const std::string& blockSelector) + { + vtkSMColorMapEditorHelper::RemoveBlocksColorArrays(proxy, { blockSelector }); + } + static void RemoveBlocksColorArrays( + vtkSMProxy* proxy, const std::vector& blockSelectors); + static ColorArray GetColorArray(vtkSMProxy* proxy); + static ColorArray GetBlockColorArray(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetBlocksColorArrays(proxy, { blockSelector }).front(); + } ///@} ///@{ /** - * Set/Get/Remove the block lookup table proxy + * Set the block lookup table proxy */ static void SetBlockLookupTable( - vtkSMProxy* proxy, const std::string& blockSelector, vtkSMProxy* lutProxy); - static vtkSMProxy* GetBlockLookupTable(vtkSMProxy* proxy, const std::string& blockSelector); - static void RemoveBlockLookupTable(vtkSMProxy* proxy, const std::string& blockSelector); + vtkSMProxy* proxy, const std::string& blockSelector, vtkSMProxy* lutProxy) + { + vtkSMColorMapEditorHelper::SetBlocksLookupTable(proxy, { blockSelector }, lutProxy); + } + static void SetBlocksLookupTable( + vtkSMProxy* proxy, const std::vector& blockSelectors, vtkSMProxy* lutProxy); + static void RemoveBlockLookupTable(vtkSMProxy* proxy, const std::string& blockSelector) + { + vtkSMColorMapEditorHelper::RemoveBlocksLookupTables(proxy, { blockSelector }); + } + static void RemoveBlocksLookupTables( + vtkSMProxy* proxy, const std::vector& blockSelectors); ///@} + /** * Internal method to set scalar coloring, do not use directly. */ static bool SetScalarColoringInternal( vtkSMProxy* proxy, const char* arrayName, int attributeType, bool useComponent, int component); - static bool SetBlockScalarColoringInternal(vtkSMProxy* proxy, const std::string& blockSelector, - const char* arrayName, int attributeType, bool useComponent, int component); + static std::vector SetBlocksScalarColoringInternal(vtkSMProxy* proxy, + const std::vector& blockSelectors, const char* arrayName, int attributeType, + bool useComponent, int component); + std::vector SetSelectedScalarColoringInternal( + vtkSMProxy* proxy, const char* arrayName, int attributeType, bool useComponent, int component); ///@} ///@{ @@ -314,20 +814,30 @@ protected: * This is used in `UpdateScalarBarRange` to update the scalar bar range when * turning off the coloring for this representation. */ - static vtkSMProxy* GetLastLUTProxy(vtkSMProxy* proxy); - static void SetLastLUTProxy(vtkSMProxy* proxy, vtkSMProxy* lutProxy); - static vtkSMProxy* GetLastBlockLUTProxy(vtkSMProxy* proxy, const std::string& blockSelector); - static void SetLastBlockLUTProxy( - vtkSMProxy* proxy, const std::string& blockSelector, vtkSMProxy* lutProxy); + static vtkSMProxy* GetLastLookupTable(vtkSMProxy* proxy); + static void SetLastLookupTable(vtkSMProxy* proxy, vtkSMProxy* lutProxy); + static vtkSMProxy* GetLastBlockLookupTable(vtkSMProxy* proxy, const std::string& blockSelector) + { + return vtkSMColorMapEditorHelper::GetLastBlocksLookupTables(proxy, { blockSelector }).front(); + } + static std::vector GetLastBlocksLookupTables( + vtkSMProxy* proxy, const std::vector& blockSelectors); + static void SetLastBlockLookupTable( + vtkSMProxy* proxy, const std::string& blockSelector, vtkSMProxy* lutProxy) + { + vtkSMColorMapEditorHelper::SetLastBlocksLookupTable(proxy, { blockSelector }, lutProxy); + } + static void SetLastBlocksLookupTable( + vtkSMProxy* proxy, const std::vector& blockSelectors, vtkSMProxy* lutProxy); ///@} friend class vtkSMPVRepresentationProxy; private: - vtkSMColorMapEditorHelper() = delete; vtkSMColorMapEditorHelper(const vtkSMColorMapEditorHelper&) = delete; - ~vtkSMColorMapEditorHelper() = delete; void operator=(const vtkSMColorMapEditorHelper&) = delete; + + int SelectedPropertiesType = SelectedPropertiesTypes::Representation; }; #endif diff --git a/Remoting/Views/vtkSMPVRepresentationProxy.cxx b/Remoting/Views/vtkSMPVRepresentationProxy.cxx index 28d2e8c1a7f..616bc50f09f 100644 --- a/Remoting/Views/vtkSMPVRepresentationProxy.cxx +++ b/Remoting/Views/vtkSMPVRepresentationProxy.cxx @@ -14,12 +14,13 @@ #include "vtkSMColorMapEditorHelper.h" #include "vtkSMProperty.h" #include "vtkSMPropertyHelper.h" +#include "vtkSMScalarBarWidgetRepresentationProxy.h" #include "vtkSMSessionProxyManager.h" #include "vtksys/SystemTools.hxx" #include -vtkCxxSetSmartPointerMacro(vtkSMPVRepresentationProxy, LastLUTProxy, vtkSMProxy); +vtkCxxSetSmartPointerMacro(vtkSMPVRepresentationProxy, LastLookupTable, vtkSMProxy); vtkStandardNewMacro(vtkSMPVRepresentationProxy); //---------------------------------------------------------------------------- @@ -27,30 +28,46 @@ vtkSMPVRepresentationProxy::vtkSMPVRepresentationProxy() { this->SetSIClassName("vtkSIPVRepresentationProxy"); this->InReadXMLAttributes = false; - this->LastLUTProxy = nullptr; + this->LastLookupTable = nullptr; } //---------------------------------------------------------------------------- vtkSMPVRepresentationProxy::~vtkSMPVRepresentationProxy() = default; //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMPVRepresentationProxy::GetLastLUTProxy() +vtkSMProxy* vtkSMPVRepresentationProxy::GetLastLookupTable() { - return this->LastLUTProxy; + return this->LastLookupTable; } //---------------------------------------------------------------------------- -void vtkSMPVRepresentationProxy::SetLastBlockLUTProxy( - vtkSMProxy* proxy, const std::string& blockSelector) +void vtkSMPVRepresentationProxy::SetLastBlocksLookupTable( + const std::vector& blockSelectors, vtkSMProxy* proxy) { - this->LastBlockLUTProxies[blockSelector] = proxy; + for (const auto& selector : blockSelectors) + { + this->LastBlocksLookupTables[selector] = proxy; + } } //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMPVRepresentationProxy::GetLastBlockLUTProxy(const std::string& blockSelector) +std::vector vtkSMPVRepresentationProxy::GetLastBlocksLookupTables( + const std::vector& blockSelectors) { - const auto& iter = this->LastBlockLUTProxies.find(blockSelector); - return iter != this->LastBlockLUTProxies.end() ? iter->second : nullptr; + std::vector proxies; + for (const auto& selector : blockSelectors) + { + auto found = this->LastBlocksLookupTables.find(selector); + if (found == this->LastBlocksLookupTables.end()) + { + proxies.push_back(nullptr); + } + else + { + proxies.push_back(found->second); + } + } + return proxies; } //---------------------------------------------------------------------------- @@ -175,9 +192,10 @@ bool vtkSMPVRepresentationProxy::GetUsingScalarColoring() } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::GetBlockUsingScalarColoring(const std::string& blockSelector) +std::vector vtkSMPVRepresentationProxy::GetBlocksUsingScalarColoring( + const std::vector& blockSelector) { - return vtkSMColorMapEditorHelper::GetBlockUsingScalarColoring(this, blockSelector); + return vtkSMColorMapEditorHelper::GetBlocksUsingScalarColoring(this, blockSelector); } //---------------------------------------------------------------------------- @@ -199,11 +217,11 @@ bool vtkSMPVRepresentationProxy::RescaleTransferFunctionToDataRange(bool extend, } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::RescaleBlockTransferFunctionToDataRange( - const std::string& blockSelector, bool extend, bool force) +std::vector vtkSMPVRepresentationProxy::RescaleBlocksTransferFunctionToDataRange( + const std::vector& blockSelectors, bool extend, bool force) { - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange( - this, blockSelector, extend, force); + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + this, blockSelectors, extend, force); } //---------------------------------------------------------------------------- @@ -215,12 +233,12 @@ bool vtkSMPVRepresentationProxy::RescaleTransferFunctionToDataRange( } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::RescaleBlockTransferFunctionToDataRange( - const std::string& blockSelector, const char* arrayName, int attributeType, bool extend, - bool force) +std::vector vtkSMPVRepresentationProxy::RescaleBlocksTransferFunctionToDataRange( + const std::vector& blockSelectors, const char* arrayName, int attributeType, + bool extend, bool force) { - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRange( - this, blockSelector, arrayName, attributeType, extend, force); + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRange( + this, blockSelectors, arrayName, attributeType, extend, force); } //---------------------------------------------------------------------------- @@ -230,11 +248,12 @@ bool vtkSMPVRepresentationProxy::RescaleTransferFunctionToDataRangeOverTime() } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::RescaleBlockTransferFunctionToDataRangeOverTime( - const std::string& blockSelector) +std::vector +vtkSMPVRepresentationProxy::RescaleBlocksTransferFunctionToDataRangeOverTime( + const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRangeOverTime( - this, blockSelector); + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + this, blockSelectors); } //---------------------------------------------------------------------------- @@ -246,11 +265,12 @@ bool vtkSMPVRepresentationProxy::RescaleTransferFunctionToDataRangeOverTime( } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::RescaleBlockTransferFunctionToDataRangeOverTime( - const std::string& blockSelector, const char* arrayName, int attributeType) +std::vector +vtkSMPVRepresentationProxy::RescaleBlocksTransferFunctionToDataRangeOverTime( + const std::vector& blockSelectors, const char* arrayName, int attributeType) { - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToDataRangeOverTime( - this, blockSelector, arrayName, attributeType); + return vtkSMColorMapEditorHelper::RescaleBlocksTransferFunctionToDataRangeOverTime( + this, blockSelectors, arrayName, attributeType); } //---------------------------------------------------------------------------- @@ -266,14 +286,6 @@ bool vtkSMPVRepresentationProxy::RescaleTransferFunctionToVisibleRange(vtkSMProx return vtkSMColorMapEditorHelper::RescaleTransferFunctionToVisibleRange(this, view); } -//---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::RescaleBlockTransferFunctionToVisibleRange( - vtkSMProxy* view, const std::string& blockSelector) -{ - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToVisibleRange( - this, view, blockSelector); -} - //---------------------------------------------------------------------------- bool vtkSMPVRepresentationProxy::RescaleTransferFunctionToVisibleRange( vtkSMProxy* view, const char* arrayName, int attributeType) @@ -282,14 +294,6 @@ bool vtkSMPVRepresentationProxy::RescaleTransferFunctionToVisibleRange( this, view, arrayName, attributeType); } -//---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::RescaleBlockTransferFunctionToVisibleRange( - vtkSMProxy* view, const std::string& blockSelector, const char* arrayName, int attributeType) -{ - return vtkSMColorMapEditorHelper::RescaleBlockTransferFunctionToVisibleRange( - this, view, blockSelector, arrayName, attributeType); -} - //---------------------------------------------------------------------------- bool vtkSMPVRepresentationProxy::SetScalarColoring(const char* arrayName, int attributeType) { @@ -297,11 +301,11 @@ bool vtkSMPVRepresentationProxy::SetScalarColoring(const char* arrayName, int at } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::SetBlockScalarColoring( - const std::string& blockSelector, const char* arrayName, int attributeType) +std::vector vtkSMPVRepresentationProxy::SetBlocksScalarColoring( + const std::vector& blockSelectors, const char* arrayName, int attributeType) { - return vtkSMColorMapEditorHelper::SetBlockScalarColoring( - this, blockSelector, arrayName, attributeType); + return vtkSMColorMapEditorHelper::SetBlocksScalarColoring( + this, blockSelectors, arrayName, attributeType); } //---------------------------------------------------------------------------- @@ -312,11 +316,12 @@ bool vtkSMPVRepresentationProxy::SetScalarColoring( } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::SetBlockScalarColoring( - const std::string& blockSelector, const char* arrayName, int attributeType, int component) +std::vector vtkSMPVRepresentationProxy::SetBlocksScalarColoring( + const std::vector& blockSelectors, const char* arrayName, int attributeType, + int component) { - return vtkSMColorMapEditorHelper::SetBlockScalarColoring( - this, blockSelector, arrayName, attributeType, component); + return vtkSMColorMapEditorHelper::SetBlocksScalarColoring( + this, blockSelectors, arrayName, attributeType, component); } //---------------------------------------------------------------------------- @@ -340,10 +345,10 @@ int vtkSMPVRepresentationProxy::IsScalarBarStickyVisible(vtkSMProxy* view) } //---------------------------------------------------------------------------- -int vtkSMPVRepresentationProxy::IsBlockScalarBarStickyVisible( - vtkSMProxy* view, const std::string& blockSelector) +std::vector vtkSMPVRepresentationProxy::IsBlocksScalarBarStickyVisible( + vtkSMProxy* view, const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::IsBlockScalarBarStickyVisible(this, view, blockSelector); + return vtkSMColorMapEditorHelper::IsBlocksScalarBarStickyVisible(this, view, blockSelectors); } //---------------------------------------------------------------------------- @@ -353,24 +358,23 @@ bool vtkSMPVRepresentationProxy::UpdateScalarBarRange(vtkSMProxy* view, bool del } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::UpdateBlockScalarBarRange( - vtkSMProxy* view, const std::string& blockSelector, bool deleteRange) +std::vector vtkSMPVRepresentationProxy::UpdateBlocksScalarBarRange( + vtkSMProxy* view, bool deleteRange) { - return vtkSMColorMapEditorHelper::UpdateBlockScalarBarRange( - this, view, blockSelector, deleteRange); + return vtkSMColorMapEditorHelper::UpdateBlocksScalarBarRange(this, view, deleteRange); } //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMPVRepresentationProxy::GetLUTProxy(vtkSMProxy* view) +vtkSMProxy* vtkSMPVRepresentationProxy::GetLookupTable(vtkSMProxy* view) { - return vtkSMColorMapEditorHelper::GetLUTProxy(this, view); + return vtkSMColorMapEditorHelper::GetLookupTable(this, view); } //---------------------------------------------------------------------------- -vtkSMProxy* vtkSMPVRepresentationProxy::GetBlockLUTProxy( - vtkSMProxy* view, const std::string& blockSelector) +std::vector vtkSMPVRepresentationProxy::GetBlocksLookupTables( + vtkSMProxy* view, const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::GetBlockLUTProxy(this, view, blockSelector); + return vtkSMColorMapEditorHelper::GetBlocksLookupTables(this, view, blockSelectors); } //---------------------------------------------------------------------------- @@ -380,10 +384,11 @@ bool vtkSMPVRepresentationProxy::SetScalarBarVisibility(vtkSMProxy* view, bool v } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::SetBlockScalarBarVisibility( - vtkSMProxy* view, const std::string& blockSelector, bool visible) +std::vector vtkSMPVRepresentationProxy::SetBlocksScalarBarVisibility( + vtkSMProxy* view, const std::vector& blockSelectors, bool visible) { - return vtkSMColorMapEditorHelper::SetBlockScalarBarVisibility(this, view, blockSelector, visible); + return vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility( + this, view, blockSelectors, visible); } //---------------------------------------------------------------------------- @@ -392,6 +397,12 @@ bool vtkSMPVRepresentationProxy::HideScalarBarIfNotNeeded(vtkSMProxy* view) return vtkSMColorMapEditorHelper::HideScalarBarIfNotNeeded(this, view); } +//---------------------------------------------------------------------------- +bool vtkSMPVRepresentationProxy::HideBlocksScalarBarIfNotNeeded(vtkSMProxy* view) +{ + return vtkSMColorMapEditorHelper::HideBlocksScalarBarIfNotNeeded(this, view); +} + //---------------------------------------------------------------------------- bool vtkSMPVRepresentationProxy::IsScalarBarVisible(vtkSMProxy* view) { @@ -399,10 +410,10 @@ bool vtkSMPVRepresentationProxy::IsScalarBarVisible(vtkSMProxy* view) } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::IsBlockScalarBarVisible( - vtkSMProxy* view, const std::string& blockSelector) +std::vector vtkSMPVRepresentationProxy::IsBlocksScalarBarVisible( + vtkSMProxy* view, const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::IsBlockScalarBarVisible(this, view, blockSelector); + return vtkSMColorMapEditorHelper::IsBlocksScalarBarVisible(this, view, blockSelectors); } //---------------------------------------------------------------------------- @@ -413,11 +424,11 @@ vtkPVArrayInformation* vtkSMPVRepresentationProxy::GetArrayInformationForColorAr } //---------------------------------------------------------------------------- -vtkPVArrayInformation* vtkSMPVRepresentationProxy::GetBlockArrayInformationForColorArray( - const std::string& blockSelector, bool checkRepresentedData) +std::vector +vtkSMPVRepresentationProxy::GetBlocksArrayInformationForColorArray( + const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray( - this, blockSelector, checkRepresentedData); + return vtkSMColorMapEditorHelper::GetBlocksArrayInformationForColorArray(this, blockSelectors); } //---------------------------------------------------------------------------- @@ -430,12 +441,13 @@ vtkSMPVRepresentationProxy::GetProminentValuesInformationForColorArray( } //---------------------------------------------------------------------------- -vtkPVProminentValuesInformation* -vtkSMPVRepresentationProxy::GetBlockProminentValuesInformationForColorArray( - const std::string& blockSelector, double uncertaintyAllowed, double fraction, bool force) +std::vector +vtkSMPVRepresentationProxy::GetBlocksProminentValuesInformationForColorArray( + const std::vector& blockSelectors, double uncertaintyAllowed, double fraction, + bool force) { - return vtkSMColorMapEditorHelper::GetBlockProminentValuesInformationForColorArray( - this, blockSelector, uncertaintyAllowed, fraction, force); + return vtkSMColorMapEditorHelper::GetBlocksProminentValuesInformationForColorArray( + this, blockSelectors, uncertaintyAllowed, fraction, force); } //---------------------------------------------------------------------------- @@ -515,11 +527,11 @@ int vtkSMPVRepresentationProxy::GetEstimatedNumberOfAnnotationsOnScalarBar(vtkSM } //---------------------------------------------------------------------------- -int vtkSMPVRepresentationProxy::GetBlockEstimatedNumberOfAnnotationsOnScalarBar( - vtkSMProxy* view, const std::string& blockSelector) +std::vector vtkSMPVRepresentationProxy::GetBlocksEstimatedNumberOfAnnotationsOnScalarBars( + vtkSMProxy* view, const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::GetBlockEstimatedNumberOfAnnotationsOnScalarBar( - this, view, blockSelector); + return vtkSMColorMapEditorHelper::GetBlocksEstimatedNumberOfAnnotationsOnScalarBars( + this, view, blockSelectors); } //---------------------------------------------------------------------------- @@ -546,7 +558,8 @@ void vtkSMPVRepresentationProxy::ViewUpdated(vtkSMProxy* view) { if (this->GetProperty("Visibility")) { - if (vtkSMPropertyHelper(this, "Visibility").GetAsInt() && this->GetUsingScalarColoring()) + const bool visible = vtkSMPropertyHelper(this, "Visibility").GetAsInt() != 0; + if (visible && this->GetUsingScalarColoring()) { this->UpdateScalarBarRange(view, false /* deleteRange */); } @@ -554,33 +567,35 @@ void vtkSMPVRepresentationProxy::ViewUpdated(vtkSMProxy* view) { this->UpdateScalarBarRange(view, true /* deleteRange */); } + if (visible && this->GetAnyBlockUsingScalarColoring()) + { + this->UpdateBlocksScalarBarRange(view, false /* deleteRange */); + } + else + { + this->UpdateBlocksScalarBarRange(view, true /* deleteRange */); + } } this->Superclass::ViewUpdated(view); } //---------------------------------------------------------------------------- -void vtkSMPVRepresentationProxy::SetBlockColorArray( - const std::string& blockSelector, int attributeType, std::string arrayName) -{ - vtkSMColorMapEditorHelper::SetBlockColorArray(this, blockSelector, attributeType, arrayName); -} - -//---------------------------------------------------------------------------- -std::pair vtkSMPVRepresentationProxy::GetBlockColorArray( - const std::string& blockSelector) +std::vector> vtkSMPVRepresentationProxy::GetBlocksColorArrays( + const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::GetBlockColorArray(this, blockSelector); + return vtkSMColorMapEditorHelper::GetBlocksColorArrays(this, blockSelectors); } //---------------------------------------------------------------------------- -void vtkSMPVRepresentationProxy::SetBlockUseSeparateColorMap( - const std::string& blockSelector, bool use) +void vtkSMPVRepresentationProxy::SetBlocksUseSeparateColorMap( + const std::vector& blockSelectors, bool use) { - vtkSMColorMapEditorHelper::SetBlockUseSeparateColorMap(this, blockSelector, use); + vtkSMColorMapEditorHelper::SetBlocksUseSeparateColorMap(this, blockSelectors, use); } //---------------------------------------------------------------------------- -bool vtkSMPVRepresentationProxy::GetBlockUseSeparateColorMap(const std::string& blockSelector) +std::vector vtkSMPVRepresentationProxy::GetBlocksUseSeparateColorMaps( + const std::vector& blockSelectors) { - return vtkSMColorMapEditorHelper::GetBlockUseSeparateColorMap(this, blockSelector); + return vtkSMColorMapEditorHelper::GetBlocksUseSeparateColorMaps(this, blockSelectors); } diff --git a/Remoting/Views/vtkSMPVRepresentationProxy.h b/Remoting/Views/vtkSMPVRepresentationProxy.h index 48aa3c6a9e3..441a8dec131 100644 --- a/Remoting/Views/vtkSMPVRepresentationProxy.h +++ b/Remoting/Views/vtkSMPVRepresentationProxy.h @@ -2,8 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause /** * @class vtkSMPVRepresentationProxy - * @brief representation for "Render View" like - * views in ParaView. + * @brief representation for "Render View" like views in ParaView. * * vtkSMPVRepresentationProxy combines surface representation and volume * representation proxies typically used for displaying data. @@ -45,10 +44,23 @@ public: * Set/get last LUT proxy. * Used as a memory of what was the last LUT proxy linked to this representation. */ - void SetLastLUTProxy(vtkSMProxy* proxy); - vtkSMProxy* GetLastLUTProxy(); - void SetLastBlockLUTProxy(vtkSMProxy* proxy, const std::string& blockSelector); - vtkSMProxy* GetLastBlockLUTProxy(const std::string& blockSelector); + PARAVIEW_DEPRECATED_IN_5_13_0("Use SetLastLookupTable instead") + void SetLastLUTProxy(vtkSMProxy* proxy) { this->SetLastLookupTable(proxy); } + void SetLastLookupTable(vtkSMProxy* proxy); + PARAVIEW_DEPRECATED_IN_5_13_0("Use GetLastLookupTable instead") + vtkSMProxy* GetLastLUTProxy() { return this->GetLastLookupTable(); } + vtkSMProxy* GetLastLookupTable(); + void SetLastBlockLookupTable(const std::string& blockSelector, vtkSMProxy* proxy) + { + this->SetLastBlocksLookupTable({ blockSelector }, proxy); + } + void SetLastBlocksLookupTable(const std::vector& blockSelectors, vtkSMProxy* proxy); + vtkSMProxy* GetLastBlockLookupTable(const std::string& blockSelector) + { + return this->GetLastBlocksLookupTables({ blockSelector }).front(); + } + std::vector GetLastBlocksLookupTables( + const std::vector& blockSelectors); ///@} ///@{ @@ -58,7 +70,12 @@ public: * check for the validity of the array. */ virtual bool GetUsingScalarColoring(); - virtual bool GetBlockUsingScalarColoring(const std::string& blockSelector); + virtual bool GetBlockUsingScalarColoring(const std::string& blockSelector) + { + return this->GetBlocksUsingScalarColoring({ blockSelector }).front(); + } + virtual std::vector GetBlocksUsingScalarColoring( + const std::vector& blockSelectors); virtual bool GetAnyBlockUsingScalarColoring(); ///@} @@ -67,8 +84,15 @@ public: * Returns the lut proxy of this representation in the given view. * This method will return `nullptr` if no lut proxy exists in this view. */ - vtkSMProxy* GetLUTProxy(vtkSMProxy* view); - vtkSMProxy* GetBlockLUTProxy(vtkSMProxy* view, const std::string& blockSelector); + PARAVIEW_DEPRECATED_IN_5_13_0("Use GetLookupTable instead") + vtkSMProxy* GetLUTProxy(vtkSMProxy* view) { return this->GetLookupTable(view); } + vtkSMProxy* GetLookupTable(vtkSMProxy* view); + vtkSMProxy* GetBlockLookupTable(vtkSMProxy* view, const std::string& blockSelector) + { + return this->GetBlocksLookupTables(view, { blockSelector }).front(); + } + std::vector GetBlocksLookupTables( + vtkSMProxy* view, const std::vector& blockSelectors); ///@} ///@{ @@ -100,8 +124,7 @@ public: * with the new range value. */ bool UpdateScalarBarRange(vtkSMProxy* view, bool deleteRange); - bool UpdateBlockScalarBarRange( - vtkSMProxy* view, const std::string& blockSelector, bool deleteRange); + std::vector UpdateBlocksScalarBarRange(vtkSMProxy* view, bool deleteRange); ///@} ///@{ @@ -113,22 +136,33 @@ public: */ virtual bool SetScalarColoring(const char* arrayName, int attributeType); virtual bool SetBlockScalarColoring( - const std::string& blockSelector, const char* arrayName, int attributeType); + const std::string& blockSelector, const char* arrayName, int attributeType) + { + return this->SetBlocksScalarColoring({ blockSelector }, arrayName, attributeType).front(); + } + virtual std::vector SetBlocksScalarColoring( + const std::vector& blockSelectors, const char* arrayName, int attributeType); ///@} ///@{ /** * Enable/disable scalar coloring using the specified array. This will set up a * color and opacity transfer functions using vtkSMTransferFunctionProxy - * instance. If arrayName is nullptr, then scalar coloring is turned off. - * \param arrayName the name of the array. - * \param attributeType must be one of vtkDataObject::AttributeTypes. - * \param component enables choosing a component to color with, - * -1 will change to Magnitude, >=0 will change to corresponding component. + * instance. If arrayName is nullptr, then scalar coloring is turned off. if + * component name is -1, then the magnitude of the array is used. If component + * name is >=0, then the corresponding component is used. */ virtual bool SetScalarColoring(const char* arrayName, int attributeType, int component); virtual bool SetBlockScalarColoring( - const std::string& blockSelector, const char* arrayName, int attributeType, int component); + const std::string& blockSelector, const char* arrayName, int attributeType, int component) + { + return this->SetBlocksScalarColoring({ blockSelector }, arrayName, attributeType, component) + .front(); + } + virtual std::vector SetBlocksScalarColoring( + const std::vector& blockSelectors, const char* arrayName, int attributeType, + int component); + ///@} ///@{ /** @@ -174,7 +208,12 @@ public: */ virtual bool RescaleTransferFunctionToDataRange(bool extend = false, bool force = true); virtual bool RescaleBlockTransferFunctionToDataRange( - const std::string& blockSelector, bool extend = false, bool force = true); + const std::string& blockSelector, bool extend = false, bool force = true) + { + return this->RescaleBlocksTransferFunctionToDataRange({ blockSelector }, extend, force).front(); + } + virtual std::vector RescaleBlocksTransferFunctionToDataRange( + const std::vector& blockSelectors, bool extend = false, bool force = true); ///@} ///@{ @@ -198,7 +237,16 @@ public: virtual bool RescaleTransferFunctionToDataRange( const char* arrayName, int attributeType, bool extend = false, bool force = true); virtual bool RescaleBlockTransferFunctionToDataRange(const std::string& blockSelector, - const char* arrayName, int attributeType, bool extend = false, bool force = true); + const char* arrayName, int attributeType, bool extend = false, bool force = true) + { + return this + ->RescaleBlocksTransferFunctionToDataRange( + { blockSelector }, arrayName, attributeType, extend, force) + .front(); + } + virtual std::vector RescaleBlocksTransferFunctionToDataRange( + const std::vector& blockSelectors, const char* arrayName, int attributeType, + bool extend = false, bool force = true); ///@} ///@{ @@ -236,7 +284,12 @@ public: * current data range over time. Returns true if rescale was successful. */ virtual bool RescaleTransferFunctionToDataRangeOverTime(); - virtual bool RescaleBlockTransferFunctionToDataRangeOverTime(const std::string& blockSelector); + virtual bool RescaleBlockTransferFunctionToDataRangeOverTime(const std::string& blockSelector) + { + return this->RescaleBlocksTransferFunctionToDataRangeOverTime({ blockSelector }).front(); + } + virtual std::vector RescaleBlocksTransferFunctionToDataRangeOverTime( + const std::vector& blockSelectors); ///@} ///@{ @@ -248,7 +301,15 @@ public: */ virtual bool RescaleTransferFunctionToDataRangeOverTime(const char* arrayName, int attributeType); virtual bool RescaleBlockTransferFunctionToDataRangeOverTime( - const std::string& blockSelector, const char* arrayName, int attributeType); + const std::string& blockSelector, const char* arrayName, int attributeType) + { + return this + ->RescaleBlocksTransferFunctionToDataRangeOverTime( + { blockSelector }, arrayName, attributeType) + .front(); + } + virtual std::vector RescaleBlocksTransferFunctionToDataRangeOverTime( + const std::vector& blockSelectors, const char* arrayName, int attributeType); ///@} ///@{ @@ -285,12 +346,8 @@ public: * using the current data range, limited to the currernt visible elements. */ virtual bool RescaleTransferFunctionToVisibleRange(vtkSMProxy* view); - virtual bool RescaleBlockTransferFunctionToVisibleRange( - vtkSMProxy* view, const std::string& blockSelector); virtual bool RescaleTransferFunctionToVisibleRange( vtkSMProxy* view, const char* arrayName, int attributeType); - virtual bool RescaleBlockTransferFunctionToVisibleRange( - vtkSMProxy* view, const std::string& blockSelector, const char* arrayName, int attributeType); ///@} ///@{ @@ -320,16 +377,19 @@ public: * Scalar bar is only shown if scalar coloring is indeed being used. */ virtual bool SetScalarBarVisibility(vtkSMProxy* view, bool visible); - virtual bool SetBlockScalarBarVisibility( - vtkSMProxy* view, const std::string& blockSelector, bool visible); PARAVIEW_DEPRECATED_IN_5_12_0("Use static functions from vtkSMColorMapEditorHelper instead") static bool SetScalarBarVisibility(vtkSMProxy* proxy, vtkSMProxy* view, bool visible) { vtkSMPVRepresentationProxy* self = vtkSMPVRepresentationProxy::SafeDownCast(proxy); return self ? self->SetScalarBarVisibility(view, visible) : false; } - ///@} - + virtual bool SetBlockScalarBarVisibility( + vtkSMProxy* view, const std::string& blockSelector, bool visible) + { + return this->SetBlocksScalarBarVisibility(view, { blockSelector }, visible).front(); + } + virtual std::vector SetBlocksScalarBarVisibility( + vtkSMProxy* view, const std::vector& blockSelectors, bool visible); ///@} ///@{ @@ -347,6 +407,7 @@ public: vtkSMPVRepresentationProxy* self = vtkSMPVRepresentationProxy::SafeDownCast(repr); return self ? self->HideScalarBarIfNotNeeded(view) : false; } + virtual bool HideBlocksScalarBarIfNotNeeded(vtkSMProxy* view); ///@} ///@{ @@ -355,13 +416,18 @@ public: * representation and view is visible, return false otherwise. */ virtual bool IsScalarBarVisible(vtkSMProxy* view); - virtual bool IsBlockScalarBarVisible(vtkSMProxy* view, const std::string& blockSelector); PARAVIEW_DEPRECATED_IN_5_12_0("Use static functions from vtkSMColorMapEditorHelper instead") static bool IsScalarBarVisible(vtkSMProxy* repr, vtkSMProxy* view) { vtkSMPVRepresentationProxy* self = vtkSMPVRepresentationProxy::SafeDownCast(repr); return self ? self->IsScalarBarVisible(view) : false; } + virtual bool IsBlockScalarBarVisible(vtkSMProxy* view, const std::string& blockSelector) + { + return this->IsBlocksScalarBarVisible(view, { blockSelector }).front(); + } + virtual std::vector IsBlocksScalarBarVisible( + vtkSMProxy* view, const std::vector& blockSelectors); ///@} ///@{ @@ -371,8 +437,6 @@ public: * If none is found, returns nullptr. */ virtual vtkPVArrayInformation* GetArrayInformationForColorArray(bool checkRepresentedData = true); - virtual vtkPVArrayInformation* GetBlockArrayInformationForColorArray( - const std::string& blockSelector, bool checkRepresentedData = true); PARAVIEW_DEPRECATED_IN_5_12_0("Use static functions from vtkSMColorMapEditorHelper instead") static vtkPVArrayInformation* GetArrayInformationForColorArray( vtkSMProxy* proxy, bool checkRepresentedData = true) @@ -380,6 +444,13 @@ public: vtkSMPVRepresentationProxy* self = vtkSMPVRepresentationProxy::SafeDownCast(proxy); return self ? self->GetArrayInformationForColorArray(checkRepresentedData) : nullptr; } + virtual vtkPVArrayInformation* GetBlockArrayInformationForColorArray( + const std::string& blockSelector) + { + return this->GetBlocksArrayInformationForColorArray({ blockSelector }).front(); + } + virtual std::vector GetBlocksArrayInformationForColorArray( + const std::vector& blockSelectors); ///@} ///@{ @@ -389,9 +460,6 @@ public: */ virtual vtkPVProminentValuesInformation* GetProminentValuesInformationForColorArray( double uncertaintyAllowed = 1e-6, double fraction = 1e-3, bool force = false); - virtual vtkPVProminentValuesInformation* GetBlockProminentValuesInformationForColorArray( - const std::string& blockSelector, double uncertaintyAllowed = 1e-6, double fraction = 1e-3, - bool force = false); PARAVIEW_DEPRECATED_IN_5_12_0("Use static functions from vtkSMColorMapEditorHelper instead") static vtkPVProminentValuesInformation* GetProminentValuesInformationForColorArray( vtkSMProxy* proxy, double uncertaintyAllowed = 1e-6, double fraction = 1e-3, bool force = false) @@ -401,20 +469,39 @@ public: ? self->GetProminentValuesInformationForColorArray(uncertaintyAllowed, fraction, force) : nullptr; } + virtual vtkPVProminentValuesInformation* GetBlockProminentValuesInformationForColorArray( + const std::string& blockSelector, double uncertaintyAllowed = 1e-6, double fraction = 1e-3, + bool force = false) + { + return this + ->GetBlocksProminentValuesInformationForColorArray( + { blockSelector }, uncertaintyAllowed, fraction, force) + .front(); + } + virtual std::vector + GetBlocksProminentValuesInformationForColorArray(const std::vector& blockSelectors, + double uncertaintyAllowed = 1e-6, double fraction = 1e-3, bool force = false); ///@} + ///@{ /** * Get an estimated number of annotation shown on this representation scalar bar */ int GetEstimatedNumberOfAnnotationsOnScalarBar(vtkSMProxy* view); - int GetBlockEstimatedNumberOfAnnotationsOnScalarBar( - vtkSMProxy* view, const std::string& blockSelector); PARAVIEW_DEPRECATED_IN_5_12_0("Use static functions from vtkSMColorMapEditorHelper instead") static int GetEstimatedNumberOfAnnotationsOnScalarBar(vtkSMProxy* proxy, vtkSMProxy* view) { vtkSMPVRepresentationProxy* self = vtkSMPVRepresentationProxy::SafeDownCast(proxy); return self ? self->GetEstimatedNumberOfAnnotationsOnScalarBar(view) : -1; } + int GetBlockEstimatedNumberOfAnnotationsOnScalarBar( + vtkSMProxy* view, const std::string& blockSelector) + { + return this->GetBlocksEstimatedNumberOfAnnotationsOnScalarBars(view, { blockSelector }).front(); + } + std::vector GetBlocksEstimatedNumberOfAnnotationsOnScalarBars( + vtkSMProxy* view, const std::vector& blockSelectors); + ///@} /** * Overridden to ensure when picking representation types that require scalar @@ -440,7 +527,12 @@ public: * it returns -1. */ int IsScalarBarStickyVisible(vtkSMProxy* view); - int IsBlockScalarBarStickyVisible(vtkSMProxy* view, const std::string& blockSelector); + int IsBlockScalarBarStickyVisible(vtkSMProxy* view, const std::string& blockSelector) + { + return this->IsBlocksScalarBarStickyVisible(view, { blockSelector }).front(); + } + std::vector IsBlocksScalarBarStickyVisible( + vtkSMProxy* view, const std::vector& blockSelectors); ///@} /** @@ -452,9 +544,12 @@ public: /** * Set/Get the block color array name. */ - void SetBlockColorArray( - const std::string& blockSelector, int attributeType, std::string arrayName); - std::pair GetBlockColorArray(const std::string& blockSelector); + std::pair GetBlockColorArray(const std::string& blockSelector) + { + return this->GetBlocksColorArrays({ blockSelector }).front(); + } + std::vector> GetBlocksColorArrays( + const std::vector& blockSelectors); int GetBlockColorArrayAssociation(const std::string& blockSelector) { return this->GetBlockColorArray(blockSelector).first; @@ -469,8 +564,17 @@ public: /** * Set/Get if we should use a separate color map for this block. */ - void SetBlockUseSeparateColorMap(const std::string& blockSelector, bool use); - bool GetBlockUseSeparateColorMap(const std::string& blockSelector); + void SetBlockUseSeparateColorMap(const std::string& blockSelector, bool use) + { + this->SetBlocksUseSeparateColorMap({ blockSelector }, use); + } + void SetBlocksUseSeparateColorMap(const std::vector& blockSelectors, bool use); + bool GetBlockUseSeparateColorMap(const std::string& blockSelector) + { + return this->GetBlocksUseSeparateColorMaps({ blockSelector }).front(); + } + std::vector GetBlocksUseSeparateColorMaps( + const std::vector& blockSelectors); ///@} protected: @@ -528,8 +632,8 @@ protected: * This is used in `UpdateScalarBarRange` to update the scalar bar range when * turning off the coloring for this representation. */ - vtkSmartPointer LastLUTProxy; - std::unordered_map> LastBlockLUTProxies; + vtkSmartPointer LastLookupTable; + std::unordered_map> LastBlocksLookupTables; private: vtkSMPVRepresentationProxy(const vtkSMPVRepresentationProxy&) = delete; diff --git a/Remoting/Views/vtkSMTransferFunctionManager.cxx b/Remoting/Views/vtkSMTransferFunctionManager.cxx index 41ffca67359..18f619f6cc1 100644 --- a/Remoting/Views/vtkSMTransferFunctionManager.cxx +++ b/Remoting/Views/vtkSMTransferFunctionManager.cxx @@ -356,15 +356,15 @@ bool vtkSMTransferFunctionManager::UpdateScalarBars(vtkSMProxy* viewProxy, unsig colored_reprs.insert(proxy); luts.insert(lut); } - const vtkSMPropertyHelper blockLookupTableSelectors(proxy, "BlockLookupTableSelectors", true); + const vtkSMPropertyHelper blockColorArrayNames(proxy, "BlockColorArrayNames", true); const vtkSMPropertyHelper blockLookUpTables(proxy, "BlockLookupTables", true); - for (unsigned int i = 0; i < blockLookupTableSelectors.GetNumberOfElements(); ++i) + for (unsigned int i = 0; i < blockLookUpTables.GetNumberOfElements(); ++i) { vtkSMProxy* blockLut = blockLookUpTables.GetAsProxy(i); if (blockLut && luts.find(blockLut) == luts.end()) { colored_reprs_blocks.insert( - std::make_pair(proxy, blockLookupTableSelectors.GetAsString(i))); + std::make_pair(proxy, blockColorArrayNames.GetAsString(3 * i))); luts.insert(blockLut); } } @@ -426,14 +426,14 @@ bool vtkSMTransferFunctionManager::UpdateScalarBarsComponentTitle( { ret |= result; } - if (representation->GetProperty("BlockLookupTableSelectors")) + if (representation->GetProperty("BlockColorArrayNames")) { - vtkSMPropertyHelper blockLookUpTableSelectors(representation, "BlockLookupTableSelectors"); - for (unsigned int i = 0; i < blockLookUpTableSelectors.GetNumberOfElements(); ++i) + vtkSMPropertyHelper blockColorArrayNames(representation, "BlockColorArrayNames"); + for (unsigned int i = 0; i < blockColorArrayNames.GetNumberOfElements(); i += 3) { if (auto result = vtkSMTransferFunctionProxy::UpdateScalarBarsComponentTitle(lutProxy, vtkSMColorMapEditorHelper::GetBlockArrayInformationForColorArray( - representation, blockLookUpTableSelectors.GetAsString(i)))) + representation, blockColorArrayNames.GetAsString(i)))) { ret |= result; } -- GitLab From 5a0a466cac89f0f80d9a31da0ceb3dd84bdca419 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 9 May 2024 10:56:27 -0400 Subject: [PATCH 08/43] pqProxyWidget: Remove not needed includes --- Qt/Components/pqProxyWidget.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/Qt/Components/pqProxyWidget.cxx b/Qt/Components/pqProxyWidget.cxx index 2732f95fdd1..ce3a69a5cbb 100644 --- a/Qt/Components/pqProxyWidget.cxx +++ b/Qt/Components/pqProxyWidget.cxx @@ -5,9 +5,6 @@ #include "pqApplicationCore.h" #include "pqCommandPropertyWidget.h" -#include "pqDisplayPanel.h" -#include "pqDisplayPanelInterface.h" -#include "pqDisplayPanelPropertyWidget.h" #include "pqDoubleVectorPropertyWidget.h" #include "pqIntVectorPropertyWidget.h" #include "pqInterfaceTracker.h" -- GitLab From c4050459e2e2de78c30cbcbde5344c03e00371e3 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 10 May 2024 17:04:31 -0400 Subject: [PATCH 09/43] vtkSIProxyProperty: Avoid using a set/map Using set/map creates issues because you can not have duplicate elements which can be useful. --- Remoting/ServerManager/vtkSIProxyProperty.cxx | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/Remoting/ServerManager/vtkSIProxyProperty.cxx b/Remoting/ServerManager/vtkSIProxyProperty.cxx index 9e8afee1c07..303bd5ee9fd 100644 --- a/Remoting/ServerManager/vtkSIProxyProperty.cxx +++ b/Remoting/ServerManager/vtkSIProxyProperty.cxx @@ -6,25 +6,26 @@ #include "vtkLogger.h" #include "vtkObjectFactory.h" #include "vtkPVXMLElement.h" -#include "vtkSIObject.h" #include "vtkSIProxy.h" #include "vtkSMMessage.h" #include "vtkSmartPointer.h" +#include "vtkType.h" #include #include #include -#include +#include +#include //****************************************************************************/ // Internal Classes and typedefs //****************************************************************************/ -class vtkSIProxyProperty::InternalCache : public std::set +class vtkSIProxyProperty::InternalCache : public std::vector { }; class vtkSIProxyProperty::vtkObjectCache - : public std::map> + : public std::vector>> { }; @@ -116,13 +117,13 @@ bool vtkSIProxyProperty::Push(vtkSMMessage* message, int offset) const ProxyState_Property* prop = &message->GetExtension(ProxyState::property, offset); assert(strcmp(prop->name().c_str(), this->GetXMLName()) == 0); - std::set new_value; + std::vector new_value; for (int cc = 0; cc < prop->value().proxy_global_id_size(); cc++) { - new_value.insert(prop->value().proxy_global_id(cc)); + new_value.push_back(prop->value().proxy_global_id(cc)); } - std::set to_add = new_value; + std::vector to_add = new_value; vtkClientServerStream stream; vtkObjectBase* object = this->GetVTKObject(); @@ -137,48 +138,63 @@ bool vtkSIProxyProperty::Push(vtkSMMessage* message, int offset) } else if (this->RemoveCommand) { - std::set to_remove; - std::set_difference(this->Cache->begin(), this->Cache->end(), new_value.begin(), - new_value.end(), std::inserter(to_remove, to_remove.begin())); + std::vector to_remove; + for (const vtkTypeUInt32& cache_id : *this->Cache) + { + if (std::find(new_value.begin(), new_value.end(), cache_id) == new_value.end()) + { + to_remove.push_back(cache_id); + } + } - for (std::set::iterator iter = to_remove.begin(); iter != to_remove.end(); - ++iter) + for (const vtkTypeUInt32& id : to_remove) { - vtkObjectBase* arg = this->GetObjectBase(*iter); + vtkObjectBase* arg = this->GetObjectBase(id); if (arg == nullptr) { - arg = (*this->ObjectCache)[*iter].GetPointer(); + arg = std::find_if(this->ObjectCache->begin(), this->ObjectCache->end(), + [id](std::pair>& pair) { + return pair.first == id; + })->second; } if (arg != nullptr) { stream << vtkClientServerStream::Invoke << object << this->GetRemoveCommand() << arg << vtkClientServerStream::End; - this->ObjectCache->erase(*iter); + this->ObjectCache->erase(std::find_if(this->ObjectCache->begin(), this->ObjectCache->end(), + [id](std::pair>& pair) { + return pair.first == id; + })); } else { - vtkWarningMacro("Failed to locate vtkObjectBase for id : " << *iter); + vtkWarningMacro("Failed to locate vtkObjectBase for id : " << id); } } to_add.clear(); - std::set_difference(new_value.begin(), new_value.end(), this->Cache->begin(), - this->Cache->end(), std::inserter(to_add, to_add.begin())); + for (const vtkTypeUInt32& id : new_value) + { + if (std::find(this->Cache->begin(), this->Cache->end(), id) == this->Cache->end()) + { + to_add.push_back(id); + } + } } // Deal with proxy to add - for (std::set::iterator iter = to_add.begin(); iter != to_add.end(); ++iter) + for (const vtkTypeUInt32& id : to_add) { - vtkObjectBase* arg = this->GetObjectBase(*iter); - if (arg != nullptr || this->IsValidNull(*iter)) + vtkObjectBase* arg = this->GetObjectBase(id); + if (arg != nullptr || this->IsValidNull(id)) { stream << vtkClientServerStream::Invoke << object << this->GetCommand() << arg << vtkClientServerStream::End; // we keep an object cache so that even if the object gets unregistered // before the property is pushed, we have a reference to it. - (*this->ObjectCache)[*iter] = arg; + this->ObjectCache->emplace_back(id, arg); } else { -- GitLab From 60d4f5787bcae7d52ec2aa3689b2050593e3511a Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 12:13:39 -0400 Subject: [PATCH 10/43] vtkSMParaViewPipelineControllerWithRendering: Hide/Setup Block colors maps --- Qt/ApplicationComponents/pqCopyReaction.cxx | 1 + .../Views/vtkSMParaViewPipelineControllerWithRendering.cxx | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Qt/ApplicationComponents/pqCopyReaction.cxx b/Qt/ApplicationComponents/pqCopyReaction.cxx index 5a37aee02bf..12ef4cb8cc4 100644 --- a/Qt/ApplicationComponents/pqCopyReaction.cxx +++ b/Qt/ApplicationComponents/pqCopyReaction.cxx @@ -403,6 +403,7 @@ void copyDescendants(pqObjectBuilder* builder, pqPipelineSource* source, pqPipel if (repr) { vtkSMColorMapEditorHelper::SetupLookupTable(repr->getProxy()); + vtkSMColorMapEditorHelper::SetupBlocksLookupTables(repr->getProxy()); } } diff --git a/Remoting/Views/vtkSMParaViewPipelineControllerWithRendering.cxx b/Remoting/Views/vtkSMParaViewPipelineControllerWithRendering.cxx index 70adbc2ae04..737b0034b03 100644 --- a/Remoting/Views/vtkSMParaViewPipelineControllerWithRendering.cxx +++ b/Remoting/Views/vtkSMParaViewPipelineControllerWithRendering.cxx @@ -137,8 +137,7 @@ void vtkInheritRepresentationProperties(vtkSMRepresentationProxy* repr, vtkSMSou return; } - // Irrespective of other properties, scalar coloring is inherited if - // possible. + // Irrespective of other properties, scalar coloring is inherited if possible. if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(inputRepr) && !vtkSMColorMapEditorHelper::GetUsingScalarColoring(repr)) { @@ -349,6 +348,7 @@ bool vtkSMParaViewPipelineControllerWithRendering::RegisterRepresentationProxy(v } vtkSMColorMapEditorHelper::SetupLookupTable(proxy); + vtkSMColorMapEditorHelper::SetupBlocksLookupTables(proxy); return true; } @@ -536,6 +536,7 @@ void vtkSMParaViewPipelineControllerWithRendering::Hide(vtkSMProxy* repr, vtkSMV if (vtkSMParaViewPipelineControllerWithRendering::HideScalarBarOnHide) { vtkSMColorMapEditorHelper::HideScalarBarIfNotNeeded(repr, view); + vtkSMColorMapEditorHelper::HideBlocksScalarBarIfNotNeeded(repr, view); } } } -- GitLab From 88b9b2e820f6e05dd84d5833023768383d04763f Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 12:52:04 -0400 Subject: [PATCH 11/43] pq(Data/Pipeline)Representation: Add block signals/functions --- .../pqRenderViewSelectionReaction.cxx | 16 ++-- .../pqRenderViewSelectionReaction.h | 1 - Qt/Core/pqDataRepresentation.cxx | 89 ++++++++++++++----- Qt/Core/pqDataRepresentation.h | 19 +++- Qt/Core/pqPipelineRepresentation.cxx | 16 ++-- Qt/Core/pqPipelineRepresentation.h | 4 +- 6 files changed, 108 insertions(+), 37 deletions(-) diff --git a/Qt/ApplicationComponents/pqRenderViewSelectionReaction.cxx b/Qt/ApplicationComponents/pqRenderViewSelectionReaction.cxx index b156e69e836..d35da24dd76 100644 --- a/Qt/ApplicationComponents/pqRenderViewSelectionReaction.cxx +++ b/Qt/ApplicationComponents/pqRenderViewSelectionReaction.cxx @@ -278,15 +278,19 @@ void pqRenderViewSelectionReaction::setRepresentation(pqDataRepresentation* repr if (this->Representation != nullptr) { - QObject::disconnect(this->RepresentationConnection); + this->disconnect(this->Representation); + this->Representation = nullptr; } - this->Representation = representation; - - if (this->Representation != nullptr) + if (representation) { - this->RepresentationConnection = this->connect( - this->Representation, SIGNAL(colorArrayNameModified()), SLOT(updateEnableState())); + this->Representation = representation; + + if (this->Representation != nullptr) + { + QObject::connect(this->Representation, &pqDataRepresentation::colorArrayNameModified, this, + &pqRenderViewSelectionReaction::updateEnableState); + } } // update enable state. diff --git a/Qt/ApplicationComponents/pqRenderViewSelectionReaction.h b/Qt/ApplicationComponents/pqRenderViewSelectionReaction.h index 63473f90528..6eb6c7d2146 100644 --- a/Qt/ApplicationComponents/pqRenderViewSelectionReaction.h +++ b/Qt/ApplicationComponents/pqRenderViewSelectionReaction.h @@ -185,7 +185,6 @@ private: Q_DISABLE_COPY(pqRenderViewSelectionReaction) QPointer View; QPointer Representation; - QMetaObject::Connection RepresentationConnection; SelectionMode Mode; int PreviousRenderViewMode; vtkWeakPointer ObservedObject; diff --git a/Qt/Core/pqDataRepresentation.cxx b/Qt/Core/pqDataRepresentation.cxx index 53e9a3d388f..2661937c1c0 100644 --- a/Qt/Core/pqDataRepresentation.cxx +++ b/Qt/Core/pqDataRepresentation.cxx @@ -3,33 +3,27 @@ // SPDX-License-Identifier: BSD-3-Clause #include "pqDataRepresentation.h" -#include "vtkDataObject.h" +#include "pqApplicationCore.h" +#include "pqOutputPort.h" +#include "pqPipelineFilter.h" +#include "pqScalarsToColors.h" +#include "pqServer.h" +#include "pqServerManagerModel.h" + #include "vtkEventQtSlotConnect.h" #include "vtkNew.h" -#include "vtkPVArrayInformation.h" #include "vtkPVDataInformation.h" -#include "vtkPVDataSetAttributesInformation.h" -#include "vtkPVGeneralSettings.h" -#include "vtkPVProminentValuesInformation.h" +#include "vtkSMColorMapEditorHelper.h" #include "vtkSMInputProperty.h" #include "vtkSMPropertyHelper.h" #include "vtkSMRepresentationProxy.h" #include "vtkSMSourceProxy.h" -#include "vtkSMStringVectorProperty.h" #include "vtkSMTransferFunctionManager.h" #include #include #include -#include "pqApplicationCore.h" -#include "pqOutputPort.h" -#include "pqPipelineFilter.h" -#include "pqSMAdaptor.h" -#include "pqScalarsToColors.h" -#include "pqServer.h" -#include "pqServerManagerModel.h" - //----------------------------------------------------------------------------- class pqDataRepresentationInternal { @@ -57,10 +51,20 @@ pqDataRepresentation::pqDataRepresentation( vtkconnector->Connect( prop, vtkCommand::ModifiedEvent, this, SIGNAL(colorTransferFunctionModified())); } + if (vtkSMProperty* prop = repr->GetProperty("BlockLookupTables")) + { + vtkconnector->Connect( + prop, vtkCommand::ModifiedEvent, this, SIGNAL(blockColorTransferFunctionModified())); + } if (vtkSMProperty* prop = repr->GetProperty("ColorArrayName")) { vtkconnector->Connect(prop, vtkCommand::ModifiedEvent, this, SIGNAL(colorArrayNameModified())); } + if (vtkSMProperty* prop = repr->GetProperty("BlockColorArrayNames")) + { + vtkconnector->Connect( + prop, vtkCommand::ModifiedEvent, this, SIGNAL(blockColorArrayNameModified())); + } if (vtkSMProperty* prop = repr->GetProperty("SelectNormalArray")) { vtkconnector->Connect(prop, vtkCommand::ModifiedEvent, this, SIGNAL(attrArrayNameModified())); @@ -165,18 +169,27 @@ void pqDataRepresentation::onInputChanged() } //----------------------------------------------------------------------------- -vtkSMProxy* pqDataRepresentation::getLookupTableProxy() +std::vector pqDataRepresentation::getLookupTableProxies( + int selectedPropertiesType) const { - return pqSMAdaptor::getProxyProperty(this->getProxy()->GetProperty("LookupTable")); + vtkNew helper; + helper->SetSelectedPropertiesType(selectedPropertiesType); + return helper->GetSelectedLookupTables(this->getProxy()); } //----------------------------------------------------------------------------- -pqScalarsToColors* pqDataRepresentation::getLookupTable() +vtkSMProxy* pqDataRepresentation::getLookupTableProxy(int selectedPropertiesType) const { - pqServerManagerModel* smmodel = pqApplicationCore::instance()->getServerManagerModel(); - vtkSMProxy* lut = this->getLookupTableProxy(); + auto luts = this->getLookupTableProxies(selectedPropertiesType); + return luts.empty() ? nullptr : luts[0] /*always get the first*/; +} - return (lut ? smmodel->findItem(lut) : 0); +//----------------------------------------------------------------------------- +pqScalarsToColors* pqDataRepresentation::getLookupTable(int selectedPropertiesType) const +{ + pqServerManagerModel* smmodel = pqApplicationCore::instance()->getServerManagerModel(); + vtkSMProxy* lut = this->getLookupTableProxy(selectedPropertiesType); + return lut ? smmodel->findItem(lut) : 0; } //----------------------------------------------------------------------------- @@ -288,6 +301,27 @@ void pqDataRepresentation::updateLookupTable() int rescaleOnVisibilityChange = vtkSMPropertyHelper(lut, "RescaleOnVisibilityChange", 1).GetAsInt(0); + + auto blockLutsProp = + vtkSMProxyProperty::SafeDownCast(representationProxy->GetProperty("BlockLookupTables")); + if (blockLutsProp) + { + for (unsigned int i = 0; i < blockLutsProp->GetNumberOfProxies(); i++) + { + if (!blockLutsProp->GetProxy(i)) + { + return; + } + } + + for (unsigned int i = 0; i < blockLutsProp->GetNumberOfProxies(); i++) + { + const int blockRescaleOnVisibilityChange = + vtkSMPropertyHelper(blockLutsProp->GetProxy(i), "RescaleOnVisibilityChange", 1).GetAsInt(0); + rescaleOnVisibilityChange = rescaleOnVisibilityChange || blockRescaleOnVisibilityChange; + } + } + if (rescaleOnVisibilityChange && this->Internal->VisibilityChangedSinceLastUpdate) { this->resetAllTransferFunctionRangesUsingCurrentData(); @@ -305,4 +339,19 @@ void pqDataRepresentation::resetAllTransferFunctionRangesUsingCurrentData() vtkNew tfmgr; tfmgr->ResetAllTransferFunctionRangesUsingCurrentData(this->getServer()->proxyManager(), false); } + auto blockLutsProp = + vtkSMProxyProperty::SafeDownCast(representationProxy->GetProperty("BlockLookupTables")); + if (blockLutsProp) + { + for (unsigned int i = 0; i < blockLutsProp->GetNumberOfProxies(); i++) + { + if (blockLutsProp->GetProxy(i)) + { + vtkNew tfmgr; + tfmgr->ResetAllTransferFunctionRangesUsingCurrentData( + this->getServer()->proxyManager(), false); + return; + } + } + } } diff --git a/Qt/Core/pqDataRepresentation.h b/Qt/Core/pqDataRepresentation.h index 31b7ce45a5b..40753cb4294 100644 --- a/Qt/Core/pqDataRepresentation.h +++ b/Qt/Core/pqDataRepresentation.h @@ -73,19 +73,28 @@ public: */ bool getDataBounds(double bounds[6]); + ///@{ /** * Returns the lookuptable proxy, if any. * Most consumer displays take a lookup table. This method provides access to * the Lookup table, if one exists. + * + * @note the singular form of this method returns the first lookup table proxy */ - virtual vtkSMProxy* getLookupTableProxy(); + virtual std::vector getLookupTableProxies( + int selectedPropertiesType = 0 /*Representation*/) const; + virtual vtkSMProxy* getLookupTableProxy(int selectedPropertiesType = 0 /*Representation*/) const; + ///@} /** * Returns the pqScalarsToColors object for the lookup table proxy if any. * Most consumer displays take a lookup table. This method provides access to * the Lookup table, if one exists. + * + * @note returns the first lookup table */ - virtual pqScalarsToColors* getLookupTable(); + virtual pqScalarsToColors* getLookupTable( + int selectedPropertiesType = 0 /*Representation*/) const; /** * Returns the data size for the full-res data. @@ -107,18 +116,24 @@ Q_SIGNALS: */ void dataUpdated(); + ///@{ /** * Fired to indicate that the "LookupTable" property (if any) on the * representation was modified. */ void colorTransferFunctionModified(); + void blockColorTransferFunctionModified(); + ///@} + ///@{ /** * Signal fired to indicate that the "ColorArrayName" property (if any) on * the representation was modified. This property controls the scalar * coloring settings on the representation. */ void colorArrayNameModified(); + void blockColorArrayNameModified(); + ///@} /** * Signal fired to indicate that the rendering attribute arrays properties diff --git a/Qt/Core/pqPipelineRepresentation.cxx b/Qt/Core/pqPipelineRepresentation.cxx index 65b339977e1..863d31b92dd 100644 --- a/Qt/Core/pqPipelineRepresentation.cxx +++ b/Qt/Core/pqPipelineRepresentation.cxx @@ -41,21 +41,25 @@ void pqPipelineRepresentation::setView(pqView* view) } //----------------------------------------------------------------------------- -void pqPipelineRepresentation::resetLookupTableScalarRange() +void pqPipelineRepresentation::resetLookupTableScalarRange(int selectedPropertiesType) { + vtkNew colorMapEditorHelper; + colorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); vtkSMProxy* proxy = this->getProxy(); - if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(proxy)) + if (colorMapEditorHelper->GetAnySelectedUsingScalarColoring(proxy)) { - vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange(proxy); + colorMapEditorHelper->RescaleSelectedTransferFunctionToDataRange(proxy); } } //----------------------------------------------------------------------------- -void pqPipelineRepresentation::resetLookupTableScalarRangeOverTime() +void pqPipelineRepresentation::resetLookupTableScalarRangeOverTime(int selectedPropertiesType) { + vtkNew colorMapEditorHelper; + colorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); vtkSMProxy* proxy = this->getProxy(); - if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(proxy)) + if (colorMapEditorHelper->GetAnySelectedUsingScalarColoring(proxy)) { - vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRangeOverTime(proxy); + colorMapEditorHelper->RescaleSelectedTransferFunctionToDataRangeOverTime(proxy); } } diff --git a/Qt/Core/pqPipelineRepresentation.h b/Qt/Core/pqPipelineRepresentation.h index cb60f06dafe..1bddb1ed4ec 100644 --- a/Qt/Core/pqPipelineRepresentation.h +++ b/Qt/Core/pqPipelineRepresentation.h @@ -54,13 +54,13 @@ public Q_SLOTS: // If lookuptable is set up and is used for coloring, // then calling this method resets the table ranges to match the current // range of the selected array. - void resetLookupTableScalarRange(); + void resetLookupTableScalarRange(int selectedPropertiesType = 0 /*Representation*/); // If lookuptable is set up and is used for coloring, // then calling this method resets the table ranges to match the // range of the selected array over time. This can potentially be a slow // processes hence use with caution!!! - void resetLookupTableScalarRangeOverTime(); + void resetLookupTableScalarRangeOverTime(int selectedPropertiesType = 0 /*Representation*/); }; #endif -- GitLab From f4118a50ceb6f6e800d6d4694c8440f7ca883201 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 12:58:19 -0400 Subject: [PATCH 12/43] pqPipelineBrowserWidget: Setup blocks' coloring when visibility changes --- Qt/Components/pqPipelineBrowserWidget.cxx | 57 +++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/Qt/Components/pqPipelineBrowserWidget.cxx b/Qt/Components/pqPipelineBrowserWidget.cxx index 6f1149dd966..0b94e282a29 100644 --- a/Qt/Components/pqPipelineBrowserWidget.cxx +++ b/Qt/Components/pqPipelineBrowserWidget.cxx @@ -28,6 +28,7 @@ #include "vtkSMTransferFunctionManager.h" #include "vtkSMViewProxy.h" +#include #include #include #include @@ -338,13 +339,16 @@ void pqPipelineBrowserWidget::setVisibility(bool visible, pqOutputPort* port) // update scalar bars: show new ones if needed. Hiding of scalar bars is // taken care of by vtkSMParaViewPipelineControllerWithRendering (I still // wonder if that's the best thing to do). + const QString scalarBarError = + tr("You might have added a new scalar bar mode, you need to do something " + "here, skipping."); if (scalarBarMode != vtkPVGeneralSettings::MANUAL_SCALAR_BARS) { // This gets executed if scalar bar mode is // AUTOMATICALLY_HIDE_SCALAR_BARS or AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS if (visible && vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)) { - int stickyVisible = + const int stickyVisible = vtkSMColorMapEditorHelper::IsScalarBarStickyVisible(reprProxy, viewProxy); if (stickyVisible != -1) { @@ -360,9 +364,54 @@ void pqPipelineBrowserWidget::setVisibility(bool visible, pqOutputPort* port) } else { - std::cerr << "You might have added a new scalar bar mode, you need to do something " - "here, skipping" - << std::endl; + qCritical() << scalarBarError << "\n"; + } + } + if (visible && vtkSMColorMapEditorHelper::GetAnyBlockUsingScalarColoring(reprProxy)) + { + const auto selectors = vtkSMColorMapEditorHelper::GetColorArraysBlockSelectors(reprProxy); + const auto stickyVisibles = vtkSMColorMapEditorHelper::IsBlocksScalarBarStickyVisible( + reprProxy, viewProxy, selectors); + if (std::any_of(stickyVisibles.begin(), stickyVisibles.end(), + [](int stickyVisible) { return stickyVisible != -1; })) + { + for (size_t i = 0; i < selectors.size(); ++i) + { + if (stickyVisibles[i] != -1) + { + vtkSMColorMapEditorHelper::SetBlockScalarBarVisibility( + reprProxy, viewProxy, selectors[i], stickyVisibles[i]); + } + else if (scalarBarMode == + vtkPVGeneralSettings::AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS) + { + vtkSMColorMapEditorHelper::SetBlockScalarBarVisibility( + reprProxy, viewProxy, selectors[i], true); + } + else if (scalarBarMode == vtkPVGeneralSettings::AUTOMATICALLY_HIDE_SCALAR_BARS) + { + vtkSMColorMapEditorHelper::SetBlockScalarBarVisibility( + reprProxy, viewProxy, selectors[i], false); + } + else + { + qCritical() << scalarBarError << "\n"; + } + } + } + else if (scalarBarMode == vtkPVGeneralSettings::AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS) + { + vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility( + reprProxy, viewProxy, selectors, true); + } + else if (scalarBarMode == vtkPVGeneralSettings::AUTOMATICALLY_HIDE_SCALAR_BARS) + { + vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility( + reprProxy, viewProxy, selectors, false); + } + else + { + qCritical() << scalarBarError << "\n"; } } } -- GitLab From 80008644f12de66598634b942b8b3ab4749aafe3 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 13:02:59 -0400 Subject: [PATCH 13/43] pqRescaleScalarRangeReaction: Support block(s) coloring Also fix a bug with pointer indices: It was 1, 2 while it should have be 2, 3 as bellow. if (!tf2dProxy->GetRange(tf2dRange)) { tf2dRange[2] = 0.0; tf2dRange[3] = 1.0; } --- .../pqRescaleScalarRangeReaction.cxx | 312 ++++++++++++------ .../pqRescaleScalarRangeReaction.h | 53 ++- 2 files changed, 261 insertions(+), 104 deletions(-) diff --git a/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.cxx b/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.cxx index fdbb2c80c4b..78f39ab2490 100644 --- a/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.cxx +++ b/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.cxx @@ -12,6 +12,9 @@ #include "pqServerManagerModel.h" #include "pqTimeKeeper.h" #include "pqUndoStack.h" + +#include "vtkCommand.h" +#include "vtkNew.h" #include "vtkPVDataInformation.h" #include "vtkSMColorMapEditorHelper.h" #include "vtkSMPropertyHelper.h" @@ -21,58 +24,59 @@ #include "vtkSMTransferFunctionManager.h" #include "vtkSMTransferFunctionProxy.h" -namespace -{ -vtkSMProxy* lutProxy(pqPipelineRepresentation* repr) -{ - vtkSMProxy* reprProxy = repr ? repr->getProxy() : nullptr; - if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)) - { - return vtkSMPropertyHelper(reprProxy, "LookupTable", true).GetAsProxy(); - } - return nullptr; -} -} +#include +#include //----------------------------------------------------------------------------- pqRescaleScalarRangeReaction::pqRescaleScalarRangeReaction( QAction* parentObject, bool track_active_objects, pqRescaleScalarRangeReaction::Modes mode) : Superclass(parentObject) , Mode(mode) - , Connection(nullptr) + , ServerAddedObserverId(0) { if (track_active_objects) { QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), this, - SLOT(setRepresentation(pqDataRepresentation*))); - this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); + QOverload::of(&pqActiveObjects::representationChanged), this, + &pqRescaleScalarRangeReaction::setActiveRepresentation); + this->setActiveRepresentation(); if (this->Mode == TEMPORAL) { // Get ready to connect timekeepers with the reaction enabled state - this->Connection = vtkSmartPointer::New(); pqServerManagerModel* model = pqApplicationCore::instance()->getServerManagerModel(); - this->connect(model, SIGNAL(serverAdded(pqServer*)), SLOT(onServerAdded(pqServer*))); - this->connect( - model, SIGNAL(aboutToRemoveServer(pqServer*)), SLOT(onAboutToRemoveServer(pqServer*))); + QObject::connect(model, &pqServerManagerModel::serverAdded, this, + &pqRescaleScalarRangeReaction::onServerAdded); + QObject::connect(model, &pqServerManagerModel::aboutToRemoveServer, this, + &pqRescaleScalarRangeReaction::onAboutToRemoveServer); } } + else + { + this->updateEnableState(); + } } //----------------------------------------------------------------------------- -pqRescaleScalarRangeReaction::~pqRescaleScalarRangeReaction() +pqRescaleScalarRangeReaction::~pqRescaleScalarRangeReaction() = default; + +//----------------------------------------------------------------------------- +void pqRescaleScalarRangeReaction::setActiveRepresentation() { - if (this->Connection) - { - this->Connection->Disconnect(); - } + this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); } //----------------------------------------------------------------------------- -void pqRescaleScalarRangeReaction::setRepresentation(pqDataRepresentation* repr) +void pqRescaleScalarRangeReaction::setRepresentation( + pqDataRepresentation* repr, int selectedPropertiesType) { + if (this->Representation != nullptr && this->Representation == repr && + this->SelectedPropertiesType == selectedPropertiesType) + { + return; + } this->Representation = qobject_cast(repr); + this->SelectedPropertiesType = selectedPropertiesType; this->updateEnableState(); } @@ -97,25 +101,30 @@ void pqRescaleScalarRangeReaction::onTriggered() switch (this->Mode) { case DATA: - pqRescaleScalarRangeReaction::rescaleScalarRangeToData(this->Representation); + pqRescaleScalarRangeReaction::rescaleScalarRangeToData( + this->Representation, this->SelectedPropertiesType); break; case CUSTOM: - pqRescaleScalarRangeReaction::rescaleScalarRangeToCustom(this->Representation); + pqRescaleScalarRangeReaction::rescaleScalarRangeToCustom( + this->Representation, this->SelectedPropertiesType); break; case TEMPORAL: - pqRescaleScalarRangeReaction::rescaleScalarRangeToDataOverTime(this->Representation); + pqRescaleScalarRangeReaction::rescaleScalarRangeToDataOverTime( + this->Representation, this->SelectedPropertiesType); break; case VISIBLE: + // Visible does NOT support selectedPropertiesType == Blocks pqRescaleScalarRangeReaction::rescaleScalarRangeToVisible(this->Representation); break; } } //----------------------------------------------------------------------------- -bool pqRescaleScalarRangeReaction::rescaleScalarRangeToData(pqPipelineRepresentation* repr) +bool pqRescaleScalarRangeReaction::rescaleScalarRangeToData( + pqPipelineRepresentation* repr, int selectedPropertiesType) { if (repr == nullptr) { @@ -128,20 +137,23 @@ bool pqRescaleScalarRangeReaction::rescaleScalarRangeToData(pqPipelineRepresenta } } - BEGIN_UNDO_SET(tr("Reset transfer function ranges using data range")); - repr->resetLookupTableScalarRange(); + const QString extraInfo = + selectedPropertiesType == vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks + ? tr("Block ") + : QString(); + const QString undoText = + tr("Reset ") + extraInfo + tr("Transfer Function Ranges Using Data Range"); + BEGIN_UNDO_SET(undoText); + repr->resetLookupTableScalarRange(selectedPropertiesType); + // no need to call lut->UpdateVTKObjects(), the resetLookupTableScalarRange call will do that repr->renderViewEventually(); - if (vtkSMProxy* lut = lutProxy(repr)) - { - lut->UpdateVTKObjects(); - } END_UNDO_SET(); return true; } //----------------------------------------------------------------------------- pqRescaleScalarRangeToCustomDialog* pqRescaleScalarRangeReaction::rescaleScalarRangeToCustom( - pqPipelineRepresentation* repr) + pqPipelineRepresentation* repr, int selectedPropertiesType) { if (repr == nullptr) { @@ -159,13 +171,17 @@ pqRescaleScalarRangeToCustomDialog* pqRescaleScalarRangeReaction::rescaleScalarR auto proxy = repr->getProxy(); if (proxy->GetProperty("UseSeparateOpacityArray")) { - vtkSMPropertyHelper helper(proxy, "UseSeparateOpacityArray", true /*quiet*/); + const vtkSMPropertyHelper helper(proxy, "UseSeparateOpacityArray", true /*quiet*/); separateOpacity = helper.GetAsInt() == 1; } - vtkSMProxy* lut = lutProxy(repr); + vtkNew colorMapEditorHelper; + colorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); + const std::vector luts = + colorMapEditorHelper->GetSelectedLookupTables(repr->getProxy()); pqRescaleScalarRangeToCustomDialog* dialog = - pqRescaleScalarRangeReaction::rescaleScalarRangeToCustom(lut, separateOpacity); + pqRescaleScalarRangeReaction::rescaleScalarRangeToCustom( + luts, separateOpacity, selectedPropertiesType); if (dialog != nullptr) { QObject::connect( @@ -176,80 +192,150 @@ pqRescaleScalarRangeToCustomDialog* pqRescaleScalarRangeReaction::rescaleScalarR //----------------------------------------------------------------------------- pqRescaleScalarRangeToCustomDialog* pqRescaleScalarRangeReaction::rescaleScalarRangeToCustom( - vtkSMProxy* lut, bool separateOpacity) + std::vector luts, bool separateOpacity, int selectedPropertiesType) { - vtkSMTransferFunctionProxy* tfProxy = vtkSMTransferFunctionProxy::SafeDownCast(lut); - if (tfProxy == nullptr || lut == nullptr) + if (luts.empty()) { return nullptr; } + for (auto lut : luts) + { + auto tfProxy = vtkSMTransferFunctionProxy::SafeDownCast(lut); + if (!lut || !tfProxy) + { + return nullptr; + } + } - double range[2] = { 0, 0 }; - if (!tfProxy->GetRange(range)) + double range[2] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN }; + bool foundValidRange = false; + for (auto lut : luts) + { + double lutRange[2]; + auto tfProxy = vtkSMTransferFunctionProxy::SafeDownCast(lut); + if (tfProxy->GetRange(lutRange)) + { + range[0] = std::min(range[0], lutRange[0]); + range[1] = std::max(range[1], lutRange[1]); + foundValidRange = true; + } + } + if (!foundValidRange) { range[0] = 0; range[1] = 1.0; } - int lockInfo = 0; - vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode").Get(&lockInfo); + std::vector locksInfo(luts.size(), 0); + for (size_t i = 0; i < luts.size(); ++i) + { + vtkSMPropertyHelper(luts[i], "AutomaticRescaleRangeMode").Get(&locksInfo[i]); + } + const int lockInfo = *std::min_element(locksInfo.begin(), locksInfo.end()); + const int maxLockInfo = *std::max_element(locksInfo.begin(), locksInfo.end()); + if (maxLockInfo != lockInfo) + { + qWarning() << "Transfer functions have different lock states."; + return nullptr; + } pqRescaleScalarRangeToCustomDialog* dialog = new pqRescaleScalarRangeToCustomDialog(pqCoreUtilities::mainWidget()); dialog->setLock(lockInfo == vtkSMTransferFunctionManager::NEVER); dialog->setRange(range[0], range[1]); dialog->showOpacityControls(separateOpacity); - vtkSMTransferFunctionProxy* sofProxy = vtkSMTransferFunctionProxy::SafeDownCast( - vtkSMPropertyHelper(lut, "ScalarOpacityFunction", true).GetAsProxy()); - vtkSMTransferFunction2DProxy* tf2dProxy = vtkSMTransferFunction2DProxy::SafeDownCast( - vtkSMPropertyHelper(lut, "TransferFunction2D", true).GetAsProxy()); - if (sofProxy && true) + std::vector sofProxies; + for (auto lut : luts) + { + auto sofProxy = vtkSMTransferFunctionProxy::SafeDownCast( + vtkSMPropertyHelper(lut, "ScalarOpacityFunction", true).GetAsProxy()); + sofProxies.push_back(sofProxy); + } + const bool hasAnySof = std::any_of(sofProxies.begin(), sofProxies.end(), + [](vtkSMTransferFunctionProxy* sofProxy) { return sofProxy != nullptr; }); + std::vector tf2dProxies; + for (auto lut : luts) { - if (!sofProxy->GetRange(range)) + auto tf2dProxy = vtkSMTransferFunction2DProxy::SafeDownCast( + vtkSMPropertyHelper(lut, "TransferFunction2D", true).GetAsProxy()); + tf2dProxies.push_back(tf2dProxy); + } + if (hasAnySof) + { + foundValidRange = false; + for (size_t i = 0; i < luts.size(); ++i) + { + if (sofProxies[i]) + { + double sofRange[2]; + if (sofProxies[i]->GetRange(sofRange)) + { + range[0] = std::min(range[0], sofRange[0]); + range[1] = std::max(range[1], sofRange[1]); + foundValidRange = true; + } + } + } + if (!foundValidRange) { range[0] = 0; range[1] = 1.0; } dialog->setOpacityRange(range[0], range[1]); - }; + } dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); QObject::connect(dialog, &pqRescaleScalarRangeToCustomDialog::apply, [=]() { - BEGIN_UNDO_SET(tr("Reset transfer function ranges")); + const QString extraInfo = + selectedPropertiesType == vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks + ? tr("Block ") + : QString(); + const QString undoText = + tr("Reset ") + extraInfo + tr("Transfer Function Ranges To Custom Range"); + BEGIN_UNDO_SET(undoText); double tRange[2]; tRange[0] = dialog->minimum(); tRange[1] = dialog->maximum(); - tfProxy->RescaleTransferFunction(tRange[0], tRange[1]); - if (sofProxy) + for (size_t i = 0; i < luts.size(); ++i) { - // If we are using a separate opacity range, get those values from the GUI - if (separateOpacity) + auto tfProxy = vtkSMTransferFunctionProxy::SafeDownCast(luts[i]); + auto sofProxy = sofProxies[i]; + auto tf2dProxy = tf2dProxies[i]; + tfProxy->RescaleTransferFunction(tRange[0], tRange[1]); + if (sofProxy) { - tRange[0] = dialog->opacityMinimum(); - tRange[1] = dialog->opacityMaximum(); + // If we are using a separate opacity range, get those values from the GUI + if (separateOpacity) + { + tRange[0] = dialog->opacityMinimum(); + tRange[1] = dialog->opacityMaximum(); + } + vtkSMTransferFunctionProxy::RescaleTransferFunction(sofProxy, tRange[0], tRange[1]); } - vtkSMTransferFunctionProxy::RescaleTransferFunction(sofProxy, tRange[0], tRange[1]); - } - if (tf2dProxy) - { - double tf2dRange[4]; - if (!tf2dProxy->GetRange(tf2dRange)) + if (tf2dProxy) { - tf2dRange[1] = 0.0; - tf2dRange[2] = 1.0; + double tf2dRange[4]; + if (!tf2dProxy->GetRange(tf2dRange)) + { + tf2dRange[2] = 0.0; + tf2dRange[3] = 1.0; + } + tf2dRange[0] = tRange[0]; + tf2dRange[1] = tRange[1]; + tf2dProxy->RescaleTransferFunction(tf2dRange); + } + // disable auto-rescale of transfer function since the user has set on + // explicitly (BUG #14371). + if (dialog->doLock()) + { + for (auto lut : luts) + { + vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode") + .Set(vtkSMTransferFunctionManager::NEVER); + lut->UpdateVTKObjects(); + } } - tf2dRange[0] = tRange[0]; - tf2dRange[1] = tRange[1]; - tf2dProxy->RescaleTransferFunction(tf2dRange); - } - // disable auto-rescale of transfer function since the user has set on - // explicitly (BUG #14371). - if (dialog->doLock()) - { - vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode") - .Set(vtkSMTransferFunctionManager::NEVER); - lut->UpdateVTKObjects(); } END_UNDO_SET(); }); @@ -259,7 +345,8 @@ pqRescaleScalarRangeToCustomDialog* pqRescaleScalarRangeReaction::rescaleScalarR //----------------------------------------------------------------------------- pqRescaleScalarRangeToDataOverTimeDialog* -pqRescaleScalarRangeReaction::rescaleScalarRangeToDataOverTime(pqPipelineRepresentation* repr) +pqRescaleScalarRangeReaction::rescaleScalarRangeToDataOverTime( + pqPipelineRepresentation* repr, int selectedPropertiesType) { if (repr == nullptr) { @@ -272,10 +359,26 @@ pqRescaleScalarRangeReaction::rescaleScalarRangeToDataOverTime(pqPipelineReprese } } - int lockInfo = 0; - if (vtkSMProxy* lut = lutProxy(repr)) + vtkNew colorMapEditorHelper; + colorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); + const std::vector luts = + colorMapEditorHelper->GetSelectedLookupTables(repr->getProxy()); + if (luts.empty()) + { + return nullptr; + } + + std::vector locksInfo(luts.size(), 0); + for (size_t i = 0; i < luts.size(); ++i) { - vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode").Get(&lockInfo); + vtkSMPropertyHelper(luts[i], "AutomaticRescaleRangeMode", /*quiet=*/true).Get(&locksInfo[i]); + } + const int lockInfo = *std::min_element(locksInfo.begin(), locksInfo.end()); + const int maxLockInfo = *std::max_element(locksInfo.begin(), locksInfo.end()); + if (maxLockInfo != lockInfo) + { + qWarning() << "Transfer functions have different lock states."; + return nullptr; } pqRescaleScalarRangeToDataOverTimeDialog* dialog = new pqRescaleScalarRangeToDataOverTimeDialog(pqCoreUtilities::mainWidget()); @@ -284,18 +387,33 @@ pqRescaleScalarRangeReaction::rescaleScalarRangeToDataOverTime(pqPipelineReprese dialog->show(); QObject::connect(dialog, &pqRescaleScalarRangeToDataOverTimeDialog::apply, [=]() { - BEGIN_UNDO_SET(tr("Reset transfer function ranges using temporal data range")); - vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRangeOverTime(repr->getProxy()); + const QString extraInfo = + selectedPropertiesType == vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks + ? tr("Block ") + : QString(); + const QString undoText = + tr("Reset ") + extraInfo + tr("Transfer Function Ranges Using Temporal Data Range"); + BEGIN_UNDO_SET(undoText); + repr->resetLookupTableScalarRangeOverTime(selectedPropertiesType); // disable auto-rescale of transfer function since the user has set one // explicitly (BUG #14371). if (dialog->doLock()) { - if (vtkSMProxy* lut = lutProxy(repr)) + vtkNew colorMapEditorHelper; + colorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); + if (colorMapEditorHelper->GetAnySelectedUsingScalarColoring(repr->getProxy())) { - vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode") - .Set(vtkSMTransferFunctionManager::NEVER); - lut->UpdateVTKObjects(); + auto luts = colorMapEditorHelper->GetSelectedLookupTables(repr->getProxy()); + for (auto& lut : luts) + { + if (lut) + { + vtkSMPropertyHelper(lut, "AutomaticRescaleRangeMode") + .Set(vtkSMTransferFunctionManager::NEVER); + lut->UpdateVTKObjects(); + } + } } } repr->renderViewEventually(); @@ -325,7 +443,7 @@ bool pqRescaleScalarRangeReaction::rescaleScalarRangeToVisible(pqPipelineReprese return false; } - BEGIN_UNDO_SET(tr("Reset transfer function ranges to visible data range")); + BEGIN_UNDO_SET(tr("Reset Transfer Function Ranges To Visible Data Range")); vtkSMColorMapEditorHelper::RescaleTransferFunctionToVisibleRange( repr->getProxy(), view->getProxy()); repr->renderViewEventually(); @@ -340,8 +458,9 @@ void pqRescaleScalarRangeReaction::onServerAdded(pqServer* server) { // Connect new server timekeeper with the reaction enable state vtkSMProxy* timeKeeper = server->getTimeKeeper()->getProxy(); - this->Connection->Connect(timeKeeper->GetProperty("SuppressedTimeSources"), - vtkCommand::ModifiedEvent, this, SLOT(updateEnableState())); + this->ServerAddedObserverId = + pqCoreUtilities::connect(timeKeeper->GetProperty("SuppressedTimeSources"), + vtkCommand::ModifiedEvent, this, SLOT(updateEnableState())); } } @@ -352,7 +471,10 @@ void pqRescaleScalarRangeReaction::onAboutToRemoveServer(pqServer* server) { // Disconnect previously connected timekeeper vtkSMProxy* timeKeeper = server->getTimeKeeper()->getProxy(); - this->Connection->Disconnect(timeKeeper->GetProperty("SuppressedTimeSources"), - vtkCommand::ModifiedEvent, this, SLOT(updateEnableState())); + if (this->ServerAddedObserverId != 0) + { + timeKeeper->GetProperty("SuppressedTimeSources")->RemoveObserver(this->ServerAddedObserverId); + this->ServerAddedObserverId = 0; + } } } diff --git a/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.h b/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.h index c574f99fea3..bad533391c4 100644 --- a/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.h +++ b/Qt/ApplicationComponents/pqRescaleScalarRangeReaction.h @@ -5,16 +5,16 @@ #define pqRescaleScalarRangeReaction_h #include "pqReaction.h" -#include "vtkSmartPointer.h" #include +#include + class pqDataRepresentation; class pqPipelineRepresentation; class pqRescaleScalarRangeToCustomDialog; class pqRescaleScalarRangeToDataOverTimeDialog; class pqServer; -class vtkEventQtSlotConnect; class vtkSMProxy; /** @@ -52,9 +52,12 @@ public: * @param[in] repr The data representation to use to determine the data range. * If `nullptr`, then the active representation is used, if * available. + * @param[in] selectedPropertiesType The selected properties type to use. + * * @returns `true` if the operation was successful, otherwise `false`. */ - static bool rescaleScalarRangeToData(pqPipelineRepresentation* repr = nullptr); + static bool rescaleScalarRangeToData( + pqPipelineRepresentation* repr = nullptr, int selectedPropertiesType = 0 /*Representation*/); /** * Rescale range to a custom range. @@ -62,22 +65,43 @@ public: * @param[in] repr The representation used to determine the transfer function * to change range on. If \c repr is `nullptr`, then the active * representation is used, if available. + * @param[in] selectedPropertiesType The selected properties type to use. + * * @returns a pointer to the dialog if the operation was successful, otherwise `nullptr`. */ static pqRescaleScalarRangeToCustomDialog* rescaleScalarRangeToCustom( - pqPipelineRepresentation* repr = nullptr); + pqPipelineRepresentation* repr = nullptr, int selectedPropertiesType = 0 /*Representation*/); /** * Rescale range to a custom range. * - * @param[in] tfProxy The transfer function proxy to rescale the range on. + * @param[in] tfProxies The transfer function proxies to rescale the range on. * @param[in] separateOpacity Show controls for setting the opacity function range * separately from the color transfer function. + * @param[in] selectedPropertiesType The selected properties type to use. * * @returns a pointer to the dialog if the operation was successful, otherwise `nullptr`. */ static pqRescaleScalarRangeToCustomDialog* rescaleScalarRangeToCustom( - vtkSMProxy* tfProxy, bool separateOpacity = false); + std::vector tfProxies, bool separateOpacity = false, + int selectedPropertiesType = 0 /*Representation*/); + + /** + * Rescale range to a custom range. + * + * @param[in] tfProxy The transfer function proxy to rescale the range on. + * @param[in] separateOpacity Show controls for setting the opacity function range + * separately from the color transfer function. + * @param[in] selectedPropertiesType The selected properties type to use. + * + * @returns a pointer to the dialog if the operation was successful, otherwise `nullptr`. + */ + static pqRescaleScalarRangeToCustomDialog* rescaleScalarRangeToCustom(vtkSMProxy* tfProxy, + bool separateOpacity = false, int selectedPropertiesType = 0 /*Representation*/) + { + return rescaleScalarRangeToCustom( + std::vector(1, tfProxy), separateOpacity, selectedPropertiesType); + } /** * Rescale range to data range over time. @@ -85,10 +109,12 @@ public: * @param[in] repr The data representation to use to determine the data range. * If `nullptr`, then the active representation is used, if * available. + * @param[in] selectedPropertiesType The selected properties type to use. + * * @returns `true` if the operation was successful, otherwise `false`. */ static pqRescaleScalarRangeToDataOverTimeDialog* rescaleScalarRangeToDataOverTime( - pqPipelineRepresentation* repr = nullptr); + pqPipelineRepresentation* repr = nullptr, int selectedPropertiesType = 0 /*Representation*/); /** * Rescale range to data range for data visible in the view. @@ -96,6 +122,7 @@ public: * @param[in] repr The data representation to use to determine the data range. * If `nullptr`, then the active representation is used, if * available. + * * @returns `true` if the operation was successful, otherwise `false`. */ static bool rescaleScalarRangeToVisible(pqPipelineRepresentation* repr = nullptr); @@ -106,10 +133,17 @@ public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) */ void updateEnableState() override; + ///@{ /** * Set the data representation explicitly when track_active_objects is false. */ - void setRepresentation(pqDataRepresentation* repr); + void setRepresentation(pqDataRepresentation* repr, int selectedPropertiesType); + void setRepresentation(pqDataRepresentation* repr) + { + this->setRepresentation(repr, 0 /*Representation*/); + } + void setActiveRepresentation(); + ///@} protected: /** @@ -125,7 +159,8 @@ private: Q_DISABLE_COPY(pqRescaleScalarRangeReaction) QPointer Representation; Modes Mode; - vtkSmartPointer Connection; + unsigned long ServerAddedObserverId; + int SelectedPropertiesType; }; #endif -- GitLab From d25f14d83d1e4e5ec842587c6b272ecb31144ae5 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 13:03:30 -0400 Subject: [PATCH 14/43] pqScalarBarVisibilityReaction: Support block(s) coloring --- .../pqScalarBarVisibilityReaction.cxx | 184 +++++++++++------- .../pqScalarBarVisibilityReaction.h | 36 +++- 2 files changed, 136 insertions(+), 84 deletions(-) diff --git a/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.cxx b/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.cxx index d4d228b9224..2cd1e620682 100644 --- a/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.cxx +++ b/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.cxx @@ -5,132 +5,161 @@ #include "pqActiveObjects.h" #include "pqDataRepresentation.h" -#include "pqRenderViewBase.h" #include "pqTimer.h" #include "pqUndoStack.h" +#include "pqView.h" + #include "vtkAbstractArray.h" #include "vtkSMColorMapEditorHelper.h" #include "vtkSMPropertyHelper.h" #include "vtkSMTransferFunctionProxy.h" +#include +#include #include #include +#include +#include +#include -#include +#include //----------------------------------------------------------------------------- pqScalarBarVisibilityReaction::pqScalarBarVisibilityReaction( QAction* parentObject, bool track_active_objects) : Superclass(parentObject) - , BlockSignals(false) - , TrackActiveObjects(track_active_objects) , Timer(new pqTimer(this)) { parentObject->setCheckable(true); - this->Timer->setSingleShot(true); this->Timer->setInterval(0); - this->connect(this->Timer, SIGNAL(timeout()), SLOT(updateEnableState())); - - QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), this, SLOT(updateEnableState()), - Qt::QueuedConnection); + QObject::connect( + this->Timer, &pqTimer::timeout, this, &pqScalarBarVisibilityReaction::updateEnableState); - this->updateEnableState(); + if (track_active_objects) + { + QObject::connect(&pqActiveObjects::instance(), + QOverload::of(&pqActiveObjects::representationChanged), this, + &pqScalarBarVisibilityReaction::setActiveRepresentation, Qt::QueuedConnection); + this->setActiveRepresentation(); + } + else + { + this->updateEnableState(); + } } //----------------------------------------------------------------------------- -pqScalarBarVisibilityReaction::~pqScalarBarVisibilityReaction() -{ - delete this->Timer; -} +pqScalarBarVisibilityReaction::~pqScalarBarVisibilityReaction() = default; //----------------------------------------------------------------------------- -void pqScalarBarVisibilityReaction::updateEnableState() +pqDataRepresentation* pqScalarBarVisibilityReaction::representation() const { - pqDataRepresentation* cachedRepr = this->CachedRepresentation; - this->setRepresentation( - this->TrackActiveObjects ? pqActiveObjects::instance().activeRepresentation() : cachedRepr); + return this->Representation; } //----------------------------------------------------------------------------- -pqDataRepresentation* pqScalarBarVisibilityReaction::representation() const +void pqScalarBarVisibilityReaction::setActiveRepresentation() { - return this->CachedRepresentation; + this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); } //----------------------------------------------------------------------------- -void pqScalarBarVisibilityReaction::setRepresentation(pqDataRepresentation* repr) +void pqScalarBarVisibilityReaction::setRepresentation( + pqDataRepresentation* repr, int selectedPropertiesType) { - if (this->CachedRepresentation) + if (this->Representation != nullptr && this->Representation == repr && + this->ColorMapEditorHelper->GetSelectedPropertiesType() == selectedPropertiesType) { - this->CachedRepresentation->disconnect(this->Timer); - this->Timer->disconnect(this->CachedRepresentation); - this->CachedRepresentation = nullptr; + return; } - if (this->CachedView) + if (this->Representation) { - this->CachedView->disconnect(this->Timer); - this->Timer->disconnect(this->CachedView); - this->CachedView = nullptr; + this->Representation->disconnect(this->Timer); + this->Timer->disconnect(this->Representation); + this->Representation = nullptr; } - - vtkSMProxy* reprProxy = repr ? repr->getProxy() : nullptr; - pqView* view = repr ? repr->getView() : nullptr; - - bool can_show_sb = repr && vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy); - bool is_shown = false; - if (repr) + if (this->View) { - this->CachedRepresentation = repr; - this->CachedView = view; - - this->Timer->connect(repr, SIGNAL(colorTransferFunctionModified()), SLOT(start())); - this->Timer->connect(repr, SIGNAL(colorArrayNameModified()), SLOT(start())); - this->Timer->connect( - view, SIGNAL(representationVisibilityChanged(pqRepresentation*, bool)), SLOT(start())); + this->View->disconnect(this->Timer); + this->Timer->disconnect(this->View); + this->View = nullptr; } + this->ColorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); - if (can_show_sb) + if (repr && repr->getView()) { - assert(repr); - assert(view); + this->Representation = repr; + this->View = repr->getView(); + + // connect the representation's and view's signals to a timer's start, + // such that a state update occurs only when a previous start has timeout. + QObject::connect(this->View, &pqView::representationVisibilityChanged, this->Timer, + QOverload<>::of(&pqTimer::start)); + if (this->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks) + { + QObject::connect(this->Representation, + &pqDataRepresentation::blockColorTransferFunctionModified, this->Timer, + QOverload<>::of(&pqTimer::start)); + QObject::connect(this->Representation, &pqDataRepresentation::blockColorArrayNameModified, + this->Timer, QOverload<>::of(&pqTimer::start)); + } + else + { + QObject::connect(this->Representation, &pqDataRepresentation::colorTransferFunctionModified, + this->Timer, QOverload<>::of(&pqTimer::start)); + QObject::connect(this->Representation, &pqDataRepresentation::colorArrayNameModified, + this->Timer, QOverload<>::of(&pqTimer::start)); + } + } + this->updateEnableState(); +} +//----------------------------------------------------------------------------- +void pqScalarBarVisibilityReaction::updateEnableState() +{ + vtkSMProxy* reprProxy = this->Representation ? this->Representation->getProxy() : nullptr; + const bool canShowSB = this->Representation && + this->ColorMapEditorHelper->GetAnySelectedUsingScalarColoring(reprProxy); + bool isShown = false; + if (canShowSB) + { // get whether the scalar bar is currently shown. vtkSMProxy* sb = this->scalarBarProxy(); - is_shown = sb ? (vtkSMPropertyHelper(sb, "Visibility").GetAsInt() != 0) : false; + isShown = sb ? (vtkSMPropertyHelper(sb, "Visibility").GetAsInt() != 0) : false; } - QAction* parent_action = this->parentAction(); - this->BlockSignals = true; - parent_action->setEnabled(can_show_sb); - parent_action->setChecked(is_shown); - this->BlockSignals = false; + const bool prev = this->blockSignals(true); + QAction* parentAction = this->parentAction(); + parentAction->setEnabled(canShowSB); + parentAction->setChecked(isShown); + this->blockSignals(prev); } //----------------------------------------------------------------------------- -vtkSMProxy* pqScalarBarVisibilityReaction::scalarBarProxy() const +std::vector pqScalarBarVisibilityReaction::scalarBarProxies() const { - pqDataRepresentation* repr = this->CachedRepresentation; + pqDataRepresentation* repr = this->Representation; vtkSMProxy* reprProxy = repr ? repr->getProxy() : nullptr; pqView* view = repr ? repr->getView() : nullptr; - if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)) + std::vector proxies; + if (this->ColorMapEditorHelper->GetAnySelectedUsingScalarColoring(reprProxy)) { - return vtkSMTransferFunctionProxy::FindScalarBarRepresentation( - repr->getLookupTableProxy(), view->getProxy()); + auto luts = this->ColorMapEditorHelper->GetSelectedLookupTables(reprProxy); + for (auto& lut : luts) + { + proxies.push_back( + vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lut, view->getProxy())); + } } - return nullptr; + return proxies; } //----------------------------------------------------------------------------- void pqScalarBarVisibilityReaction::setScalarBarVisibility(bool visible) { - if (this->BlockSignals) - { - return; - } - - pqDataRepresentation* repr = this->CachedRepresentation; + pqDataRepresentation* repr = this->Representation; if (!repr) { qCritical() << "Required active objects are not available."; @@ -138,26 +167,31 @@ void pqScalarBarVisibilityReaction::setScalarBarVisibility(bool visible) } if (visible && - vtkSMColorMapEditorHelper::GetEstimatedNumberOfAnnotationsOnScalarBar( + this->ColorMapEditorHelper->GetAnySelectedEstimatedNumberOfAnnotationsOnScalarBar( repr->getProxy(), repr->getView()->getProxy()) > vtkAbstractArray::MAX_DISCRETE_VALUES) { - QMessageBox* box = new QMessageBox(QMessageBox::Warning, tr("Number of annotations warning"), - tr("The color map have been configured to show lots of annotations." - " Showing the scalar bar in this situation may slow down the rendering" - " and it may not be readable anyway. Do you really want to show the color map ?"), - QMessageBox::Yes | QMessageBox::No); + QPointer box = + new QMessageBox(QMessageBox::Warning, tr("Number of annotations warning"), + tr("The color map have been configured to show lots of annotations." + " Showing the scalar bar in this situation may slow down the rendering" + " and it may not be readable anyway. Do you really want to show the color map ?"), + QMessageBox::Yes | QMessageBox::No); box->move(QCursor::pos()); if (box->exec() == QMessageBox::No) { visible = false; - int blocked = this->parentAction()->blockSignals(true); + const bool blocked = this->parentAction()->blockSignals(true); this->parentAction()->setChecked(false); this->parentAction()->blockSignals(blocked); } - delete box; } - BEGIN_UNDO_SET(tr("Toggle Color Legend Visibility")); - vtkSMColorMapEditorHelper::SetScalarBarVisibility( + const QString extraInfo = this->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks + ? tr("Block ") + : QString(); + const QString undoName = tr("Toggle ") + extraInfo + tr("Color Legend Visibility"); + BEGIN_UNDO_SET(extraInfo); + this->ColorMapEditorHelper->SetSelectedScalarBarVisibility( repr->getProxy(), repr->getView()->getProxy(), visible); END_UNDO_SET(); repr->renderViewEventually(); diff --git a/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h b/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h index 2c98c59768f..9153a4a5ed4 100644 --- a/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h +++ b/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h @@ -7,8 +7,14 @@ #include "pqReaction.h" #include +#include "vtkNew.h" // For vtkNew + +#include // For std::vector + class pqDataRepresentation; class pqTimer; +class pqView; +class vtkSMColorMapEditorHelper; class vtkSMProxy; /** @@ -33,16 +39,30 @@ public: */ pqDataRepresentation* representation() const; + ///@{ /** * Returns the scalar bar for the current representation, if any. */ - vtkSMProxy* scalarBarProxy() const; + std::vector scalarBarProxies() const; + vtkSMProxy* scalarBarProxy() const + { + std::vector proxies = this->scalarBarProxies(); + return proxies.empty() ? nullptr : proxies[0]; + } + ///@} public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) + ///@{ /** - * Set the active representation. + * Set the representation. */ - void setRepresentation(pqDataRepresentation*); + void setRepresentation(pqDataRepresentation*, int selectedPropertiesType); + void setRepresentation(pqDataRepresentation* repr) + { + this->setRepresentation(repr, 0 /*Representation*/); + } + void setActiveRepresentation(); + ///@} /** * set scalar bar visibility. @@ -64,12 +84,10 @@ protected: // NOLINT(readability-redundant-access-specifiers) private: Q_DISABLE_COPY(pqScalarBarVisibilityReaction) - bool BlockSignals; - bool TrackActiveObjects; - QPointer CachedRepresentation; - QPointer CachedScalarBar; - QPointer CachedView; - pqTimer* Timer; + QPointer Representation; + QPointer View; + QPointer Timer; + vtkNew ColorMapEditorHelper; }; #endif -- GitLab From e1f074357d0ef3ecbd53ad3dcf600539fdfb7ac2 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 13:03:48 -0400 Subject: [PATCH 15/43] pqEditScalarBarReaction: Support block(s) coloring --- .../pqEditScalarBarReaction.cxx | 55 +++++++++++++++---- .../pqEditScalarBarReaction.h | 9 ++- Remoting/ServerManager/vtkSMProxy.cxx | 50 +++++++++++++++++ Remoting/ServerManager/vtkSMProxy.h | 5 ++ 4 files changed, 107 insertions(+), 12 deletions(-) diff --git a/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx b/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx index e3e24fcd8c2..418330a2442 100644 --- a/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx +++ b/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx @@ -7,26 +7,33 @@ #include "pqProxyWidgetDialog.h" #include "pqScalarBarVisibilityReaction.h" +#include "vtkSMProperty.h" +#include "vtkSMPropertyHelper.h" +#include "vtkSMProxy.h" +#include "vtkSMSessionProxyManager.h" + +#include +#include +#include + //----------------------------------------------------------------------------- pqEditScalarBarReaction::pqEditScalarBarReaction(QAction* parentObject, bool track_active_objects) : Superclass(parentObject) { QAction* tmp = new QAction(this); this->SBVReaction = new pqScalarBarVisibilityReaction(tmp, track_active_objects); - this->connect(tmp, SIGNAL(changed()), SLOT(updateEnableState())); + QObject::connect(tmp, &QAction::changed, this, &pqEditScalarBarReaction::updateEnableState); this->updateEnableState(); } //----------------------------------------------------------------------------- -pqEditScalarBarReaction::~pqEditScalarBarReaction() -{ - delete this->SBVReaction; -} +pqEditScalarBarReaction::~pqEditScalarBarReaction() = default; //----------------------------------------------------------------------------- -void pqEditScalarBarReaction::setRepresentation(pqDataRepresentation* repr) +void pqEditScalarBarReaction::setRepresentation( + pqDataRepresentation* repr, int selectedPropertiesType) { - this->SBVReaction->setRepresentation(repr); + this->SBVReaction->setRepresentation(repr, selectedPropertiesType); } //----------------------------------------------------------------------------- @@ -45,9 +52,18 @@ void pqEditScalarBarReaction::onTriggered() //----------------------------------------------------------------------------- bool pqEditScalarBarReaction::editScalarBar() { - if (vtkSMProxy* sbProxy = this->SBVReaction->scalarBarProxy()) + auto scalarBarProxies = this->SBVReaction->scalarBarProxies(); + if (scalarBarProxies.empty()) { - pqRepresentation* repr = this->SBVReaction->representation(); + return false; + } + if (vtkSMProxy* sbProxy = scalarBarProxies[0]) + { + pqDataRepresentation* repr = this->SBVReaction->representation(); + + auto copyProxy = vtk::TakeSmartPointer( + repr->proxyManager()->NewProxy(sbProxy->GetXMLGroup(), sbProxy->GetXMLName())); + copyProxy->Copy(sbProxy); pqProxyWidgetDialog dialog(sbProxy); dialog.setWindowTitle(tr("Edit Color Legend Properties")); @@ -55,8 +71,25 @@ bool pqEditScalarBarReaction::editScalarBar() dialog.setEnableSearchBar(true); dialog.setSettingsKey("ColorLegendEditor"); - repr->connect(&dialog, SIGNAL(accepted()), SLOT(renderViewEventually())); - return dialog.exec() == QDialog::Accepted; + QObject::connect( + &dialog, &pqProxyWidgetDialog::accepted, repr, &pqDataRepresentation::renderViewEventually); + const bool accepted = dialog.exec() == QDialog::Accepted; + if (accepted) + { + auto changedProperties = sbProxy->GetPropertiesWithDifferentValues(copyProxy); + for (size_t i = 1; i < scalarBarProxies.size(); ++i) + { + const auto otherSBProxy = scalarBarProxies[i]; + if (otherSBProxy && otherSBProxy != sbProxy) + { + for (const auto& prop : changedProperties) + { + otherSBProxy->GetProperty(prop.c_str())->Copy(sbProxy->GetProperty(prop.c_str())); + } + } + } + } + return accepted; } return false; } diff --git a/Qt/ApplicationComponents/pqEditScalarBarReaction.h b/Qt/ApplicationComponents/pqEditScalarBarReaction.h index 8cd77884e2b..a18f23f1b6f 100644 --- a/Qt/ApplicationComponents/pqEditScalarBarReaction.h +++ b/Qt/ApplicationComponents/pqEditScalarBarReaction.h @@ -5,6 +5,7 @@ #define pqEditScalarBarReaction_h #include "pqReaction.h" + #include class pqDataRepresentation; @@ -30,13 +31,19 @@ public: ~pqEditScalarBarReaction() override; public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) + ///@{ /** * Set the active representation. This should only be used when * \c track_active_objects is false. If used when \c track_active_objects is * true, the representation will get replaced whenever the active * representation changes. */ - void setRepresentation(pqDataRepresentation*); + void setRepresentation(pqDataRepresentation*, int selectedPropertiesType); + void setRepresentation(pqDataRepresentation* repr) + { + this->setRepresentation(repr, 0 /*Representation*/); + } + ///@} /** * Show the editor dialog for editing scalar bar properties. diff --git a/Remoting/ServerManager/vtkSMProxy.cxx b/Remoting/ServerManager/vtkSMProxy.cxx index d13b46b7104..b1f308198d4 100644 --- a/Remoting/ServerManager/vtkSMProxy.cxx +++ b/Remoting/ServerManager/vtkSMProxy.cxx @@ -23,6 +23,7 @@ #include "vtkSMMessage.h" #include "vtkSMPluginLoaderProxy.h" #include "vtkSMPropertyGroup.h" +#include "vtkSMPropertyHelper.h" #include "vtkSMPropertyIterator.h" #include "vtkSMProxyListDomain.h" #include "vtkSMProxyLocator.h" @@ -2676,3 +2677,52 @@ void vtkSMProxy::SetLogNameInternal( this->PushState(&logname_state); } } + +//--------------------------------------------------------------------------- +std::vector vtkSMProxy::GetPropertiesWithDifferentValues(vtkSMProxy* otherProxy) +{ + if (!otherProxy) + { + return std::vector(); + } + if (strcmp(this->GetXMLName(), otherProxy->GetXMLName()) != 0 || + strcmp(this->GetXMLGroup(), otherProxy->GetXMLGroup()) != 0) + { + return std::vector(); + } + + std::vector differentProperties; + auto iter = vtk::TakeSmartPointer(this->NewPropertyIterator()); + for (iter->Begin(); !iter->IsAtEnd(); iter->Next()) + { + vtkSMProperty* property = iter->GetProperty(); + vtkSMProperty* otherProperty = otherProxy->GetProperty(iter->GetKey()); + if (property && otherProperty && std::string(property->GetClassName()) != "vtkSMProperty" && + property->IsA(otherProperty->GetClassName())) + { + const vtkSMPropertyHelper helper(property); + const vtkSMPropertyHelper otherHelper(otherProperty); + bool different = false; + if (helper.GetNumberOfElements() != otherHelper.GetNumberOfElements()) + { + different = true; + } + else + { + for (unsigned int i = 0; i < helper.GetNumberOfElements(); i++) + { + if (!helper.GetAsVariant(i).IsEqual(otherHelper.GetAsVariant(i))) + { + different = true; + break; + } + } + } + if (different) + { + differentProperties.push_back(iter->GetKey()); + } + } + } + return differentProperties; +} diff --git a/Remoting/ServerManager/vtkSMProxy.h b/Remoting/ServerManager/vtkSMProxy.h index d88ed50c25b..ed228966831 100644 --- a/Remoting/ServerManager/vtkSMProxy.h +++ b/Remoting/ServerManager/vtkSMProxy.h @@ -603,6 +603,11 @@ public: bool GetPropertiesModified() { return this->PropertiesModified; } + /** + * Return the property names that have values different from the otherProxy argument. + */ + virtual std::vector GetPropertiesWithDifferentValues(vtkSMProxy* otherProxy); + protected: vtkSMProxy(); ~vtkSMProxy() override; -- GitLab From 5ed6c0c65de1390503872c9167104c9694191d31 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 13:19:35 -0400 Subject: [PATCH 16/43] pqUseSeparateColorMapReaction: Support block(s) coloring --- .../pqUseSeparateColorMapReaction.cxx | 211 ++++++++++++++---- .../pqUseSeparateColorMapReaction.h | 19 +- 2 files changed, 177 insertions(+), 53 deletions(-) diff --git a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx index 6d3be0c5693..f8fd23c40f6 100644 --- a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx +++ b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx @@ -6,7 +6,11 @@ #include "pqActiveObjects.h" #include "pqDataRepresentation.h" #include "pqDisplayColorWidget.h" +#include "pqPropertyLinksConnection.h" +#include "pqUndoStack.h" #include "pqView.h" + +#include "vtkNew.h" #include "vtkSMColorMapEditorHelper.h" #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" @@ -14,82 +18,182 @@ #include "vtkSMScalarBarWidgetRepresentationProxy.h" #include "vtkSMTransferFunctionProxy.h" +#include +#include + +//============================================================================= +/// This class makes it possible to add custom logic when updating the +/// "ColorArrayName" property instead of directly setting the SMProperty. The +/// custom logic, in this case, ensures that the LUT is setup and initialized +/// (all done by vtkSMColorMapEditorHelper::SetSelectedUseSeparateColorMap()). +class pqUseSeparateColorMapReaction::PropertyLinksConnection : public pqPropertyLinksConnection +{ +private: + using Superclass = pqPropertyLinksConnection; + using ValueType = pqDisplayColorWidget::ValueType; + + vtkNew ColorMapEditorHelper; + +public: + PropertyLinksConnection(QObject* qobject, const char* qproperty, const char* qsignal, + vtkSMProxy* smproxy, vtkSMProperty* smproperty, int smindex, bool use_unchecked_modified_event, + QObject* parentObject = nullptr) + : Superclass(qobject, qproperty, qsignal, smproxy, smproperty, smindex, + use_unchecked_modified_event, parentObject) + { + this->ColorMapEditorHelper->SetSelectedPropertiesType( + vtkSMColorMapEditorHelper::GetPropertyType(smproperty)); + } + ~PropertyLinksConnection() override = default; + +protected: + /// Called to update the ServerManager Property due to UI change. + void setServerManagerValue(bool use_unchecked, const QVariant& value) override + { + assert(use_unchecked == false); + Q_UNUSED(use_unchecked); + + const int useSep = value.toBool() ? 1 : 0; + + const QString undoText = QCoreApplication::translate("PropertyLinksConnection", "Change ") + + QCoreApplication::translate("ServerManagerXML", this->propertySM()->GetXMLLabel()); + BEGIN_UNDO_SET(undoText); + this->ColorMapEditorHelper->SetSelectedUseSeparateColorMap(this->proxySM(), useSep); + END_UNDO_SET(); + } + + /// called to get the current value for the ServerManager Property. + QVariant currentServerManagerValue(bool use_unchecked) const override + { + assert(use_unchecked == false); + Q_UNUSED(use_unchecked); + + vtkSMProxy* reprProxy = this->proxySM(); + + vtkSMProperty* colorProp = + this->ColorMapEditorHelper->GetSelectedUseSeparateColorMapProperty(reprProxy); + if (!colorProp) + { + return 0; + } + auto useSeps = this->ColorMapEditorHelper->GetSelectedUseSeparateColorMaps(reprProxy); + return !useSeps.empty() && useSeps[0] == 1 ? 1 : 0; + } + +private: + Q_DISABLE_COPY(PropertyLinksConnection) +}; + //----------------------------------------------------------------------------- pqUseSeparateColorMapReaction::pqUseSeparateColorMapReaction( QAction* parentObject, pqDisplayColorWidget* colorWidget, bool track_active_objects) : Superclass(parentObject) , ColorWidget(colorWidget) - , TrackActiveObjects(track_active_objects) - , BlockSignals(false) { parentObject->setCheckable(true); - QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), this, SLOT(updateEnableState()), - Qt::QueuedConnection); - this->updateEnableState(); + if (track_active_objects) + { + QObject::connect(&pqActiveObjects::instance(), + QOverload::of(&pqActiveObjects::representationChanged), this, + &pqUseSeparateColorMapReaction::setActiveRepresentation, Qt::QueuedConnection); + this->setActiveRepresentation(); + } + else + { + this->updateEnableState(); + } } //----------------------------------------------------------------------------- pqUseSeparateColorMapReaction::~pqUseSeparateColorMapReaction() = default; //----------------------------------------------------------------------------- -void pqUseSeparateColorMapReaction::updateEnableState() +pqDataRepresentation* pqUseSeparateColorMapReaction::representation() const { - pqDataRepresentation* cachedRepr = this->CachedRepresentation; - this->setRepresentation( - this->TrackActiveObjects ? pqActiveObjects::instance().activeRepresentation() : cachedRepr); + return this->Representation; } //----------------------------------------------------------------------------- -pqDataRepresentation* pqUseSeparateColorMapReaction::representation() const +void pqUseSeparateColorMapReaction::setActiveRepresentation() { - return this->CachedRepresentation; + this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); } //----------------------------------------------------------------------------- -void pqUseSeparateColorMapReaction::setRepresentation(pqDataRepresentation* repr) +void pqUseSeparateColorMapReaction::setRepresentation( + pqDataRepresentation* repr, int selectedPropertiesType) { + if (this->Representation != nullptr && this->Representation == repr && + this->ColorMapEditorHelper->GetSelectedPropertiesType() == selectedPropertiesType) + { + return; + } this->Links.clear(); - if (this->CachedRepresentation) + if (this->Representation) { - QObject::disconnect(this->CachedRepresentation, SIGNAL(colorArrayNameModified()), this, - SLOT(updateEnableState())); + this->disconnect(this->Representation); + this->Representation = nullptr; } - this->CachedRepresentation = repr; + this->ColorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); if (repr) { - QObject::connect(repr, SIGNAL(colorArrayNameModified()), this, SLOT(updateEnableState()), - Qt::QueuedConnection); + this->Representation = repr; + if (this->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks) + { + QObject::connect(this->Representation, &pqDataRepresentation::blockColorArrayNameModified, + this, &pqUseSeparateColorMapReaction::updateEnableState, Qt::QueuedConnection); + } + else + { + QObject::connect(this->Representation, &pqDataRepresentation::colorArrayNameModified, this, + &pqUseSeparateColorMapReaction::updateEnableState, Qt::QueuedConnection); + } } + vtkSMProxy* reprProxy = this->Representation ? this->Representation->getProxy() : nullptr; + vtkSMProperty* useSeparateProp = reprProxy + ? this->ColorMapEditorHelper->GetSelectedUseSeparateColorMapProperty(reprProxy) + : nullptr; + this->updateEnableState(); + // Create the link + if (reprProxy && useSeparateProp) + { + this->Links.addPropertyLink( + this->parentAction(), "checked", SIGNAL(toggled(bool)), reprProxy, useSeparateProp); + } +} +//----------------------------------------------------------------------------- +void pqUseSeparateColorMapReaction::updateEnableState() +{ // Recover proxy and action - vtkSMProxy* reprProxy = repr ? repr->getProxy() : nullptr; - QAction* parent_action = this->parentAction(); - - // Set action state - vtkSMProperty* colorProp = reprProxy ? reprProxy->GetProperty("UseSeparateColorMap") : nullptr; - bool can_sep = - reprProxy && colorProp && vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy); - parent_action->setEnabled(can_sep); - parent_action->setChecked(false); - if (colorProp && reprProxy) + vtkSMProxy* reprProxy = this->Representation ? this->Representation->getProxy() : nullptr; + vtkSMProperty* useSeparateProp = reprProxy + ? this->ColorMapEditorHelper->GetSelectedUseSeparateColorMapProperty(reprProxy) + : nullptr; + const bool canSep = reprProxy && useSeparateProp && + this->ColorMapEditorHelper->GetAnySelectedUsingScalarColoring(reprProxy); + bool isSep = false; + if (canSep) { - this->Links.addPropertyLink( - parent_action, "checked", SIGNAL(toggled(bool)), reprProxy, colorProp); + isSep = this->ColorMapEditorHelper->GetAnySelectedUseSeparateColorMap(reprProxy); } + // Set action state + this->parentAction()->setEnabled(canSep); + this->parentAction()->setChecked(isSep); } //----------------------------------------------------------------------------- void pqUseSeparateColorMapReaction::onTriggered() { // Disable Multi Components Mapping - pqDataRepresentation* repr = this->CachedRepresentation.data(); + pqDataRepresentation* repr = this->Representation.data(); vtkSMRepresentationProxy* proxy = vtkSMRepresentationProxy::SafeDownCast(repr->getProxy()); vtkSMProperty* mcmProperty = proxy->GetProperty("MultiComponentsMapping"); if (vtkSMPropertyHelper(mcmProperty).GetAsInt() == 1) { - vtkSMProperty* sepProperty = proxy->GetProperty("UseSeparateColorMap"); - if (vtkSMPropertyHelper(sepProperty).GetAsInt() == 0) + const auto useSep = this->ColorMapEditorHelper->GetAnySelectedUseSeparateColorMap(proxy); + if (!useSep) { vtkSMPropertyHelper(mcmProperty).Set(0); } @@ -98,25 +202,34 @@ void pqUseSeparateColorMapReaction::onTriggered() pqView* view = pqActiveObjects::instance().activeView(); vtkSMProxy* viewProxy = view ? view->getProxy() : nullptr; - if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(proxy)) + if (this->ColorMapEditorHelper->GetAnySelectedUsingScalarColoring(proxy)) { - if (vtkSMProperty* lutProperty = proxy->GetProperty("LookupTable")) + const vtkSMProperty* lutProp = this->ColorMapEditorHelper->GetSelectedColorArrayProperty(proxy); + if (!lutProp) { - vtkSMPropertyHelper lutPropertyHelper(lutProperty); - if (lutPropertyHelper.GetNumberOfElements() != 0 && lutPropertyHelper.GetAsProxy(0)) + auto luts = this->ColorMapEditorHelper->GetSelectedLookupTables(proxy); + auto selectors = this->ColorMapEditorHelper->GetSelectedBlockSelectors(proxy); + for (size_t i = 0; i < luts.size(); ++i) { - vtkSMProxy* lutProxy = lutPropertyHelper.GetAsProxy(0); - - vtkSMScalarBarWidgetRepresentationProxy* sbProxy = - vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( + if (vtkSMProxy* lutProxy = luts[i]) + { + auto sbProxy = vtkSMScalarBarWidgetRepresentationProxy::SafeDownCast( vtkSMTransferFunctionProxy::FindScalarBarRepresentation(lutProxy, viewProxy)); - - sbProxy->RemoveRange(proxy); - sbProxy->UpdateVTKObjects(); - } - else - { - qWarning("Failed to determine the LookupTable being used."); + if (this->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Representation) + { + sbProxy->RemoveRange(proxy); + } + else + { + sbProxy->RemoveBlockRange(proxy, selectors[i]); + } + sbProxy->UpdateVTKObjects(); + } + else + { + qWarning("Failed to determine the LookupTable being used."); + } } } else diff --git a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h index 229a82bcc3e..fc159f73af6 100644 --- a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h +++ b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h @@ -8,8 +8,11 @@ #include "pqReaction.h" #include +#include "vtkNew.h" // For vtkNew + class pqDataRepresentation; class pqDisplayColorWidget; +class vtkSMColorMapEditorHelper; /** * @ingroup Reactions @@ -36,10 +39,17 @@ public: pqDataRepresentation* representation() const; public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) + ///@{ /** * Set the active representation. */ - void setRepresentation(pqDataRepresentation*); + void setRepresentation(pqDataRepresentation*, int selectedPropertiesType); + void setRepresentation(pqDataRepresentation* repr) + { + this->setRepresentation(repr, 0 /*Representation*/); + } + void setActiveRepresentation(); + ///@} protected Q_SLOTS: /** @@ -55,11 +65,12 @@ protected Q_SLOTS: private: Q_DISABLE_COPY(pqUseSeparateColorMapReaction) + class PropertyLinksConnection; + pqPropertyLinks Links; - QPointer CachedRepresentation; + QPointer Representation; pqDisplayColorWidget* ColorWidget; - bool TrackActiveObjects; - bool BlockSignals; + vtkNew ColorMapEditorHelper; }; #endif -- GitLab From 22ee8fa6c86e7411a577c48c8a7d10a6b9ac0566 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 13:22:17 -0400 Subject: [PATCH 17/43] pqEditColorMapReaction: Support block(s) coloring --- .../pqEditColorMapReaction.cxx | 77 ++++++++++++------- .../pqEditColorMapReaction.h | 18 ++++- 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/Qt/ApplicationComponents/pqEditColorMapReaction.cxx b/Qt/ApplicationComponents/pqEditColorMapReaction.cxx index 370dcd22750..decee70050d 100644 --- a/Qt/ApplicationComponents/pqEditColorMapReaction.cxx +++ b/Qt/ApplicationComponents/pqEditColorMapReaction.cxx @@ -8,15 +8,18 @@ #include "pqCoreUtilities.h" #include "pqPipelineRepresentation.h" #include "pqUndoStack.h" + #include "vtkSMColorMapEditorHelper.h" #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" -#include "vtkSMTrace.h" #include #include #include #include +#include + +#include //----------------------------------------------------------------------------- pqEditColorMapReaction::pqEditColorMapReaction(QAction* parentObject, bool track_active_objects) @@ -25,15 +28,35 @@ pqEditColorMapReaction::pqEditColorMapReaction(QAction* parentObject, bool track if (track_active_objects) { QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), this, - SLOT(setRepresentation(pqDataRepresentation*))); - this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); + QOverload::of(&pqActiveObjects::representationChanged), this, + &pqEditColorMapReaction::setActiveRepresentation); + this->setActiveRepresentation(); + } + else + { + this->updateEnableState(); } } //----------------------------------------------------------------------------- -void pqEditColorMapReaction::setRepresentation(pqDataRepresentation* repr) +pqEditColorMapReaction::~pqEditColorMapReaction() = default; + +//----------------------------------------------------------------------------- +void pqEditColorMapReaction::setActiveRepresentation() +{ + this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); +} + +//----------------------------------------------------------------------------- +void pqEditColorMapReaction::setRepresentation( + pqDataRepresentation* repr, int selectedPropertiesType) { + if (this->Representation != nullptr && this->Representation == repr && + selectedPropertiesType == this->ColorMapEditorHelper->GetSelectedPropertiesType()) + { + return; + } + this->ColorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); this->Representation = qobject_cast(repr); this->updateEnableState(); } @@ -53,7 +76,7 @@ void pqEditColorMapReaction::onTriggered() //----------------------------------------------------------------------------- void pqEditColorMapReaction::editColorMap(pqPipelineRepresentation* repr) { - if (repr == nullptr) + if (!repr) { repr = qobject_cast(pqActiveObjects::instance().activeRepresentation()); @@ -64,40 +87,36 @@ void pqEditColorMapReaction::editColorMap(pqPipelineRepresentation* repr) } } - if (!vtkSMColorMapEditorHelper::GetUsingScalarColoring(repr->getProxy())) + if (!this->ColorMapEditorHelper->GetAnySelectedUsingScalarColoring(repr->getProxy())) { // Get the color property. vtkSMProxy* proxy = repr->getProxy(); - SM_SCOPED_TRACE(PropertiesModified) - .arg(proxy) - .arg("comment", qPrintable(tr(" change solid color"))); - - vtkSMProperty* diffuse = proxy->GetProperty("DiffuseColor"); - vtkSMProperty* ambient = proxy->GetProperty("AmbientColor"); - if (diffuse == nullptr && ambient == nullptr) + std::array rgb = this->ColorMapEditorHelper->GetSelectedColors(proxy)[0]; + // if no valid color found, get the color of the representation + QString extraInfo; + if (this->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks) { - diffuse = proxy->GetProperty("Color"); + extraInfo = tr("Block "); + if (!vtkSMColorMapEditorHelper::IsColorValid(rgb)) + { + rgb = this->ColorMapEditorHelper->GetColor(proxy); + } } - - if (diffuse || ambient) + if (vtkSMColorMapEditorHelper::IsColorValid(rgb)) { - // Get the current color from the property. - double rgb[3]; - vtkSMPropertyHelper(diffuse ? diffuse : ambient).Get(rgb, 3); - // Let the user pick a new color. + const QString dialogTitle = tr("Pick ") + extraInfo + tr("Solid Color"); auto color = QColorDialog::getColor(QColor::fromRgbF(rgb[0], rgb[1], rgb[2]), - pqCoreUtilities::mainWidget(), tr("Pick Solid Color"), QColorDialog::DontUseNativeDialog); + pqCoreUtilities::mainWidget(), dialogTitle, QColorDialog::DontUseNativeDialog); if (color.isValid()) { // Set the properties to the new color. - rgb[0] = color.redF(); - rgb[1] = color.greenF(); - rgb[2] = color.blueF(); - BEGIN_UNDO_SET(tr("Changed Solid Color")); - vtkSMPropertyHelper(diffuse, /*quiet=*/true).Set(rgb, 3); - vtkSMPropertyHelper(ambient, /*quiet=*/true).Set(rgb, 3); - proxy->UpdateVTKObjects(); + rgb = { color.redF(), color.greenF(), color.blueF() }; + const QString undoName = tr("Change ") + extraInfo + tr("Solid Color"); + BEGIN_UNDO_SET(undoName); + this->ColorMapEditorHelper->SetSelectedColor(proxy, rgb); + // no need to call repr->UpdateVTKObjects(), the SetSelectedColor call will do that repr->renderViewEventually(); END_UNDO_SET(); } diff --git a/Qt/ApplicationComponents/pqEditColorMapReaction.h b/Qt/ApplicationComponents/pqEditColorMapReaction.h index 48fa1ce8934..bb6350f9c25 100644 --- a/Qt/ApplicationComponents/pqEditColorMapReaction.h +++ b/Qt/ApplicationComponents/pqEditColorMapReaction.h @@ -5,11 +5,13 @@ #define pqEditColorMapReaction_h #include "pqReaction.h" +#include // For QPointer -#include +#include // For vtkNew class pqPipelineRepresentation; class pqDataRepresentation; +class vtkSMColorMapEditorHelper; /** * @ingroup Reactions @@ -26,11 +28,12 @@ public: * pqActiveObjects automatically. */ pqEditColorMapReaction(QAction* parent, bool track_active_objects = true); + ~pqEditColorMapReaction() override; /** * Edit active representation's color map (or solid color). */ - static void editColorMap(pqPipelineRepresentation* repr = nullptr); + void editColorMap(pqPipelineRepresentation* repr = nullptr); public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) /** @@ -38,10 +41,17 @@ public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) */ void updateEnableState() override; + ///@{ /** * Set the active representation. */ - void setRepresentation(pqDataRepresentation*); + void setRepresentation(pqDataRepresentation*, int selectedPropertiesType); + void setRepresentation(pqDataRepresentation* repr) + { + this->setRepresentation(repr, 0 /*Representation*/); + } + void setActiveRepresentation(); + ///@} protected: /** @@ -52,6 +62,8 @@ protected: private: Q_DISABLE_COPY(pqEditColorMapReaction) QPointer Representation; + + vtkNew ColorMapEditorHelper; }; #endif -- GitLab From 86d720d311143381f28a52de2836e663d44e69c8 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 13:55:40 -0400 Subject: [PATCH 18/43] pqChooseColorPresetReaction: Support block(s) coloring --- .../pqChooseColorPresetReaction.cxx | 195 +++++++++++------- .../pqChooseColorPresetReaction.h | 30 ++- 2 files changed, 148 insertions(+), 77 deletions(-) diff --git a/Qt/ApplicationComponents/pqChooseColorPresetReaction.cxx b/Qt/ApplicationComponents/pqChooseColorPresetReaction.cxx index 566a5afcc80..05f52de66cf 100644 --- a/Qt/ApplicationComponents/pqChooseColorPresetReaction.cxx +++ b/Qt/ApplicationComponents/pqChooseColorPresetReaction.cxx @@ -8,28 +8,21 @@ #include "pqDataRepresentation.h" #include "pqPresetDialog.h" #include "pqUndoStack.h" + #include "vtkSMColorMapEditorHelper.h" #include "vtkSMPropertyHelper.h" #include "vtkSMTransferFunctionProxy.h" #include "vtk_jsoncpp.h" +#include + +#include #include +#include QPointer pqChooseColorPresetReaction::PresetDialog; -namespace pvInternals -{ -vtkSMProxy* lutProxy(vtkSMProxy* reprProxy) -{ - if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)) - { - return vtkSMPropertyHelper(reprProxy, "LookupTable", true).GetAsProxy(); - } - return nullptr; -} -} - //----------------------------------------------------------------------------- pqChooseColorPresetReaction::pqChooseColorPresetReaction( QAction* parentObject, bool track_active_objects) @@ -39,9 +32,9 @@ pqChooseColorPresetReaction::pqChooseColorPresetReaction( if (track_active_objects) { QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), this, - SLOT(setRepresentation(pqDataRepresentation*))); - this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); + QOverload::of(&pqActiveObjects::representationChanged), this, + &pqChooseColorPresetReaction::setActiveRepresentation); + this->setActiveRepresentation(); } } @@ -49,21 +42,44 @@ pqChooseColorPresetReaction::pqChooseColorPresetReaction( pqChooseColorPresetReaction::~pqChooseColorPresetReaction() = default; //----------------------------------------------------------------------------- -void pqChooseColorPresetReaction::setRepresentation(pqDataRepresentation* repr) +void pqChooseColorPresetReaction::setActiveRepresentation() { - if (this->Representation == repr) + this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); +} + +//----------------------------------------------------------------------------- +void pqChooseColorPresetReaction::setRepresentation( + pqDataRepresentation* repr, int selectedPropertiesType) +{ + if (this->Representation == repr && + this->ColorMapEditorHelper->GetSelectedPropertiesType() == selectedPropertiesType) { return; } if (this->Representation) { this->disconnect(this->Representation); + this->Representation = nullptr; } - this->Representation = repr; + this->ColorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); if (repr) { - this->connect(repr, SIGNAL(colorTransferFunctionModified()), SLOT(updateTransferFunction())); - this->connect(repr, SIGNAL(colorArrayNameModified()), SLOT(updateTransferFunction())); + this->Representation = repr; + if (this->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks) + { + QObject::connect(repr, &pqDataRepresentation::blockColorTransferFunctionModified, this, + &pqChooseColorPresetReaction::updateTransferFunction); + QObject::connect(repr, &pqDataRepresentation::blockColorArrayNameModified, this, + &pqChooseColorPresetReaction::updateTransferFunction); + } + else + { + QObject::connect(repr, &pqDataRepresentation::colorTransferFunctionModified, this, + &pqChooseColorPresetReaction::updateTransferFunction); + QObject::connect(repr, &pqDataRepresentation::colorArrayNameModified, this, + &pqChooseColorPresetReaction::updateTransferFunction); + } } this->updateTransferFunction(); } @@ -71,21 +87,28 @@ void pqChooseColorPresetReaction::setRepresentation(pqDataRepresentation* repr) //----------------------------------------------------------------------------- void pqChooseColorPresetReaction::updateTransferFunction() { - this->setTransferFunction( - this->Representation ? this->Representation->getLookupTableProxy() : nullptr); + this->setTransferFunctions(this->Representation + ? this->ColorMapEditorHelper->GetSelectedLookupTables(this->Representation->getProxy()) + : std::vector{}); } //----------------------------------------------------------------------------- -void pqChooseColorPresetReaction::setTransferFunction(vtkSMProxy* lut) +void pqChooseColorPresetReaction::setTransferFunctions(std::vector luts) { - this->TransferFunctionProxy = lut; + this->TransferFunctionProxies.clear(); + for (auto& lut : luts) + { + this->TransferFunctionProxies.push_back(lut); + } this->updateEnableState(); } //----------------------------------------------------------------------------- void pqChooseColorPresetReaction::updateEnableState() { - this->parentAction()->setEnabled(this->TransferFunctionProxy != nullptr); + const bool enabled = std::all_of(this->TransferFunctionProxies.begin(), + this->TransferFunctionProxies.end(), [](vtkSMProxy* lut) { return lut != nullptr; }); + this->parentAction()->setEnabled(enabled); } //----------------------------------------------------------------------------- @@ -97,40 +120,56 @@ void pqChooseColorPresetReaction::onTriggered() //----------------------------------------------------------------------------- bool pqChooseColorPresetReaction::choosePreset(const char* presetName) { - vtkSMProxy* lut = this->TransferFunctionProxy; - if (!lut) + if (this->TransferFunctionProxies.empty()) { return false; } - - bool indexedLookup = vtkSMPropertyHelper(lut, "IndexedLookup", true).GetAsInt() != 0; - - if (PresetDialog) + for (auto& lut : this->TransferFunctionProxies) { - PresetDialog->setMode(indexedLookup ? pqPresetDialog::SHOW_INDEXED_COLORS_ONLY - : pqPresetDialog::SHOW_NON_INDEXED_COLORS_ONLY); + if (!lut) + { + return false; + } + } + std::vector indexedLookups(this->TransferFunctionProxies.size(), false); + for (size_t i = 0; i < this->TransferFunctionProxies.size(); ++i) + { + indexedLookups[i] = + vtkSMPropertyHelper(this->TransferFunctionProxies[i], "IndexedLookup", true).GetAsInt() != 0; + } + const bool indexedLookup = *std::min_element(indexedLookups.begin(), indexedLookups.end()); + const bool maxIndexLookup = *std::max_element(indexedLookups.begin(), indexedLookups.end()); + if (indexedLookup != maxIndexLookup) + { + qWarning("Cannot apply presets to a mix of indexed and non-indexed lookup tables."); + return false; + } + if (this->PresetDialog) + { + this->PresetDialog->setMode(indexedLookup ? pqPresetDialog::SHOW_INDEXED_COLORS_ONLY + : pqPresetDialog::SHOW_NON_INDEXED_COLORS_ONLY); } else { // This should be deleted when the mainWidget is closed and it should be impossible // to get back here with the preset dialog open due to the event filtering done by // the preset dialog. - PresetDialog = new pqPresetDialog(pqCoreUtilities::mainWidget(), + this->PresetDialog = new pqPresetDialog(pqCoreUtilities::mainWidget(), indexedLookup ? pqPresetDialog::SHOW_INDEXED_COLORS_ONLY : pqPresetDialog::SHOW_NON_INDEXED_COLORS_ONLY); } - PresetDialog->setCurrentPreset(presetName); - PresetDialog->setCustomizableLoadColors(!indexedLookup); - PresetDialog->setCustomizableLoadOpacities(!indexedLookup); - PresetDialog->setCustomizableUsePresetRange(!indexedLookup); - PresetDialog->setCustomizableLoadAnnotations(indexedLookup); - PresetDialog->setCustomizableAnnotationsRegexp(indexedLookup && this->AllowsRegexpMatching); - this->connect(PresetDialog.data(), &pqPresetDialog::applyPreset, this, + this->PresetDialog->setCurrentPreset(presetName); + this->PresetDialog->setCustomizableLoadColors(!indexedLookup); + this->PresetDialog->setCustomizableLoadOpacities(!indexedLookup); + this->PresetDialog->setCustomizableUsePresetRange(!indexedLookup); + this->PresetDialog->setCustomizableLoadAnnotations(indexedLookup); + this->PresetDialog->setCustomizableAnnotationsRegexp(indexedLookup && this->AllowsRegexpMatching); + this->connect(this->PresetDialog.data(), &pqPresetDialog::applyPreset, this, &pqChooseColorPresetReaction::applyCurrentPreset); - PresetDialog->show(); - PresetDialog->raise(); - PresetDialog->activateWindow(); + this->PresetDialog->show(); + this->PresetDialog->raise(); + this->PresetDialog->activateWindow(); return true; } @@ -139,49 +178,58 @@ void pqChooseColorPresetReaction::applyCurrentPreset() { pqPresetDialog* dialog = qobject_cast(this->sender()); assert(dialog); - assert(dialog == PresetDialog); + assert(dialog == this->PresetDialog); - vtkSMProxy* lut = this->TransferFunctionProxy; - if (!lut) + if (this->TransferFunctionProxies.empty()) { return; } + for (auto& lut : this->TransferFunctionProxies) + { + if (!lut) + { + return; + } + } BEGIN_UNDO_SET(tr("Apply color preset")); if (dialog->loadColors() || dialog->loadOpacities()) { - vtkSMProxy* sof = vtkSMPropertyHelper(lut, "ScalarOpacityFunction", true).GetAsProxy(); - if (dialog->loadColors()) + for (auto& lut : this->TransferFunctionProxies) { - vtkSMTransferFunctionProxy::ApplyPreset( - lut, dialog->currentPreset(), !dialog->usePresetRange()); - } - if (dialog->loadOpacities()) - { - if (sof) + vtkSMProxy* sof = vtkSMPropertyHelper(lut, "ScalarOpacityFunction", true).GetAsProxy(); + if (dialog->loadColors()) { vtkSMTransferFunctionProxy::ApplyPreset( - sof, dialog->currentPreset(), !dialog->usePresetRange()); + lut, dialog->currentPreset(), !dialog->usePresetRange()); } - else + if (dialog->loadOpacities()) { - qWarning("Cannot load opacities since 'ScalarOpacityFunction' is not present."); + if (sof) + { + vtkSMTransferFunctionProxy::ApplyPreset( + sof, dialog->currentPreset(), !dialog->usePresetRange()); + } + else + { + qWarning("Cannot load opacities since 'ScalarOpacityFunction' is not present."); + } } - } - // We need to take extra care to avoid the color and opacity function ranges - // from straying away from each other. This can happen if only one of them is - // getting a preset and we're using the preset range. - if (dialog->usePresetRange() && (dialog->loadColors() ^ dialog->loadOpacities()) && sof) - { - double range[2]; - if (dialog->loadColors() && vtkSMTransferFunctionProxy::GetRange(lut, range)) - { - vtkSMTransferFunctionProxy::RescaleTransferFunction(sof, range); - } - else if (dialog->loadOpacities() && vtkSMTransferFunctionProxy::GetRange(sof, range)) + // We need to take extra care to avoid the color and opacity function ranges + // from straying away from each other. This can happen if only one of them is + // getting a preset and we're using the preset range. + if (dialog->usePresetRange() && (dialog->loadColors() ^ dialog->loadOpacities()) && sof) { - vtkSMTransferFunctionProxy::RescaleTransferFunction(lut, range); + double range[2]; + if (dialog->loadColors() && vtkSMTransferFunctionProxy::GetRange(lut, range)) + { + vtkSMTransferFunctionProxy::RescaleTransferFunction(sof, range); + } + else if (dialog->loadOpacities() && vtkSMTransferFunctionProxy::GetRange(sof, range)) + { + vtkSMTransferFunctionProxy::RescaleTransferFunction(lut, range); + } } } } @@ -190,7 +238,10 @@ void pqChooseColorPresetReaction::applyCurrentPreset() // on the Lookup table if (dialog->loadAnnotations() || dialog->regularExpression().isValid()) { - vtkSMTransferFunctionProxy::ApplyPreset(lut, dialog->currentPreset(), false); + for (auto& lut : this->TransferFunctionProxies) + { + vtkSMTransferFunctionProxy::ApplyPreset(lut, dialog->currentPreset(), false); + } } END_UNDO_SET(); diff --git a/Qt/ApplicationComponents/pqChooseColorPresetReaction.h b/Qt/ApplicationComponents/pqChooseColorPresetReaction.h index 7302cdf27bd..4ec2a301d40 100644 --- a/Qt/ApplicationComponents/pqChooseColorPresetReaction.h +++ b/Qt/ApplicationComponents/pqChooseColorPresetReaction.h @@ -5,12 +5,18 @@ #define pqChooseColorPresetReaction_h #include "pqReaction.h" -#include "vtkWeakPointer.h" // needed for vtkWeakPointer. + +#include "vtkNew.h" // needed for vtkNew. +#include "vtkWeakPointer.h" // needed for vtkWeakPointer. + #include // needed for QPointer #include // needed for QRegularExpression. +#include // needed for std::vector. + class pqDataRepresentation; class pqPresetDialog; +class vtkSMColorMapEditorHelper; class vtkSMProxy; /** @@ -57,17 +63,30 @@ public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) */ bool choosePreset(const char* presetName = nullptr); + ///@{ /** * Set the data representation explicitly when track_active_objects is false. */ - void setRepresentation(pqDataRepresentation* repr); - + void setRepresentation(pqDataRepresentation* repr, int selectedPropertiesType); + void setRepresentation(pqDataRepresentation* repr) + { + this->setRepresentation(repr, 0 /*Representation*/); + } + void setActiveRepresentation(); + ///@} + + ///@{ /** * Set the transfer function proxy. This can be used, instead of * setRepresentation() when \c track_active_objects was false to directly set * the transfer function proxy on which to apply the preset. */ - void setTransferFunction(vtkSMProxy* lut); + void setTransferFunctions(std::vector lut); + void setTransferFunction(vtkSMProxy* lut) + { + this->setTransferFunctions(std::vector{ lut }); + } + ///@} /** * Updates the enabled state. Applications need not explicitly call this. @@ -115,9 +134,10 @@ protected: private: Q_DISABLE_COPY(pqChooseColorPresetReaction) QPointer Representation; - vtkWeakPointer TransferFunctionProxy; + std::vector> TransferFunctionProxies; static QPointer PresetDialog; bool AllowsRegexpMatching; + vtkNew ColorMapEditorHelper; }; #endif -- GitLab From 391dd18b582e938ac1114319184636c96f0ac35e Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 14:17:29 -0400 Subject: [PATCH 19/43] pqDisplayColorWidget: Support block(s) coloring --- Qt/ApplicationComponents/pqColorToolbar.cxx | 4 +- Qt/Components/pqDisplayColorWidget.cxx | 357 +++++++++++------- Qt/Components/pqDisplayColorWidget.h | 33 +- .../pqDisplayRepresentationWidget.cxx | 5 +- 4 files changed, 240 insertions(+), 159 deletions(-) diff --git a/Qt/ApplicationComponents/pqColorToolbar.cxx b/Qt/ApplicationComponents/pqColorToolbar.cxx index bc9f79a1940..70f036980c4 100644 --- a/Qt/ApplicationComponents/pqColorToolbar.cxx +++ b/Qt/ApplicationComponents/pqColorToolbar.cxx @@ -34,6 +34,6 @@ void pqColorToolbar::constructor() new pqUseSeparateColorMapReaction(ui.actionUseSeparateColorMap, display_color); QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), display_color, - SLOT(setRepresentation(pqDataRepresentation*))); + QOverload::of(&pqActiveObjects::representationChanged), display_color, + QOverload::of(&pqDisplayColorWidget::setRepresentation)); } diff --git a/Qt/Components/pqDisplayColorWidget.cxx b/Qt/Components/pqDisplayColorWidget.cxx index 5d0b4fd1272..3145ccf1375 100644 --- a/Qt/Components/pqDisplayColorWidget.cxx +++ b/Qt/Components/pqDisplayColorWidget.cxx @@ -3,14 +3,16 @@ // SPDX-License-Identifier: BSD-3-Clause #include "pqDisplayColorWidget.h" +#include "pqCoreUtilities.h" #include "pqDataRepresentation.h" #include "pqPropertiesPanel.h" #include "pqPropertyLinks.h" +#include "pqPropertyLinksConnection.h" #include "pqScalarsToColors.h" #include "pqUndoStack.h" -#include "pqView.h" + +#include "vtkCommand.h" #include "vtkDataObject.h" -#include "vtkEventQtSlotConnect.h" #include "vtkNew.h" #include "vtkPVArrayInformation.h" #include "vtkPVDataInformation.h" @@ -21,9 +23,9 @@ #include "vtkSMColorMapEditorHelper.h" #include "vtkSMProperty.h" #include "vtkSMPropertyHelper.h" +#include "vtkSMProxy.h" #include "vtkSMTrace.h" #include "vtkSMTransferFunctionManager.h" -#include "vtkSMTransferFunctionProxy.h" #include "vtkSMViewProxy.h" #include "vtkWeakPointer.h" @@ -31,10 +33,16 @@ #include #include #include +#include +#include +#include #include -#include +#include +#include +#include #include +#include namespace { @@ -62,11 +70,14 @@ bool supportedAssociation(int assoc) /// This class makes it possible to add custom logic when updating the /// "ColorArrayName" property instead of directly setting the SMProperty. The /// custom logic, in this case, ensures that the LUT is setup and initialized -/// (all done by vtkSMColorMapEditorHelper::SetScalarColoring()). +/// (all done by vtkSMColorMapEditorHelper::SetSelectedScalarColoring()). class pqDisplayColorWidget::PropertyLinksConnection : public pqPropertyLinksConnection { - typedef pqPropertyLinksConnection Superclass; - typedef pqDisplayColorWidget::ValueType ValueType; +private: + using Superclass = pqPropertyLinksConnection; + using ValueType = pqDisplayColorWidget::ValueType; + + vtkNew ColorMapEditorHelper; public: PropertyLinksConnection(QObject* qobject, const char* qproperty, const char* qsignal, @@ -75,6 +86,8 @@ public: : Superclass(qobject, qproperty, qsignal, smproxy, smproperty, smindex, use_unchecked_modified_event, parentObject) { + this->ColorMapEditorHelper->SetSelectedPropertiesType( + vtkSMColorMapEditorHelper::GetPropertyType(smproperty)); } ~PropertyLinksConnection() override = default; @@ -88,7 +101,7 @@ public: return ValueType(); } assert(value.canConvert()); - QStringList strList = value.toStringList(); + const QStringList strList = value.toStringList(); assert(strList.size() == 2); return ValueType(strList[0].toInt(), strList[1]); } @@ -111,36 +124,32 @@ protected: assert(use_unchecked == false); Q_UNUSED(use_unchecked); - ValueType val = this->convert(value); - int association = val.first; + const ValueType val = this->convert(value); + const int association = val.first; const QString& arrayName = val.second; - BEGIN_UNDO_SET(QCoreApplication::translate("PropertyLinksConnection", "Change coloring")); + const QString undoText = QCoreApplication::translate("PropertyLinksConnection", "Change ") + + QCoreApplication::translate("ServerManagerXML", this->propertySM()->GetXMLLabel()); + BEGIN_UNDO_SET(undoText); vtkSMProxy* reprProxy = this->proxySM(); // before changing scalar color, save the LUT being used so we can hide the // scalar bar later, if needed. - vtkSMProxy* oldLutProxy = vtkSMPropertyHelper(reprProxy, "LookupTable", true).GetAsProxy(); + const std::vector oldLutProxies = + this->ColorMapEditorHelper->GetSelectedLookupTables(reprProxy); - vtkSMColorMapEditorHelper::SetScalarColoring(reprProxy, arrayName.toUtf8().data(), association); + this->ColorMapEditorHelper->SetSelectedScalarColoring( + reprProxy, arrayName.toUtf8().data(), association); - vtkNew tmgr; - pqDisplayColorWidget* widget = qobject_cast(this->objectQt()); - vtkSMViewProxy* view = widget->viewProxy(); + vtkSMViewProxy* view = qobject_cast(this->objectQt())->viewProxy(); // Hide unused scalar bars, if applicable. - vtkPVGeneralSettings* gsettings = vtkPVGeneralSettings::GetInstance(); - switch (gsettings->GetScalarBarMode()) - { - case vtkPVGeneralSettings::AUTOMATICALLY_HIDE_SCALAR_BARS: - case vtkPVGeneralSettings::AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS: - tmgr->HideScalarBarIfNotNeeded(oldLutProxy, view); - break; - } + pqDisplayColorWidget::hideScalarBarsIfNotNeeded(view, oldLutProxies); if (!arrayName.isEmpty()) { - pqDisplayColorWidget::updateScalarBarVisibility(view, reprProxy); + pqDisplayColorWidget::updateScalarBarVisibility( + view, reprProxy, this->ColorMapEditorHelper->GetSelectedPropertiesType()); } END_UNDO_SET(); } @@ -152,11 +161,15 @@ protected: Q_UNUSED(use_unchecked); ValueType val; vtkSMProxy* reprProxy = this->proxySM(); - if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)) + if (this->ColorMapEditorHelper->GetAnySelectedUsingScalarColoring(reprProxy)) { - vtkSMPropertyHelper helper(this->propertySM()); - val.first = helper.GetInputArrayAssociation(); - val.second = helper.GetInputArrayNameToProcess(); + const auto colorArrays = this->ColorMapEditorHelper->GetSelectedColorArrays(reprProxy); + if (!colorArrays.empty()) + { + // Always get the first array. + val.first = colorArrays[0].first; + val.second = colorArrays[0].second.c_str(); + } } return this->convert(val); } @@ -164,14 +177,15 @@ protected: /// called to set the UI due to a ServerManager Property change. void setQtValue(const QVariant& value) override { - ValueType val = this->convert(value); + const ValueType val = this->convert(value); qobject_cast(this->objectQt())->setArraySelection(val); } /// called to get the UI value. QVariant currentQtValue() const override { - ValueType curVal = qobject_cast(this->objectQt())->arraySelection(); + const ValueType curVal = + qobject_cast(this->objectQt())->arraySelection(); return this->convert(curVal); } @@ -180,15 +194,37 @@ private: }; //----------------------------------------------------------------------------- -void pqDisplayColorWidget::updateScalarBarVisibility(vtkSMViewProxy* view, vtkSMProxy* reprProxy) +void pqDisplayColorWidget::hideScalarBarsIfNotNeeded( + vtkSMViewProxy* view, std::vector luts) +{ + vtkNew tmgr; + vtkPVGeneralSettings* generalSettings = vtkPVGeneralSettings::GetInstance(); + for (auto lut : luts) + { + switch (generalSettings->GetScalarBarMode()) + { + case vtkPVGeneralSettings::AUTOMATICALLY_HIDE_SCALAR_BARS: + case vtkPVGeneralSettings::AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS: + tmgr->HideScalarBarIfNotNeeded(lut, view); + break; + default: + break; + } + } +} + +//----------------------------------------------------------------------------- +void pqDisplayColorWidget::updateScalarBarVisibility( + vtkSMViewProxy* view, vtkSMProxy* reprProxy, int selectedPropertiesType) { - // we could now respect some application setting to determine if the LUT is - // to be reset. - vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( + vtkNew colorMapEditorHelper; + colorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); + // we could now respect some application setting to determine if the LUT is to be reset. + colorMapEditorHelper->RescaleSelectedTransferFunctionToDataRange( reprProxy, /*extend*/ true, /*force*/ false); /// BUG #0011858. Users often do silly things! - bool reprVisibility = + const bool reprVisibility = vtkSMPropertyHelper(reprProxy, "Visibility", /*quiet*/ true).GetAsInt() == 1; vtkPVGeneralSettings* gsettings = vtkPVGeneralSettings::GetInstance(); @@ -197,20 +233,24 @@ void pqDisplayColorWidget::updateScalarBarVisibility(vtkSMViewProxy* view, vtkSM if (reprVisibility && gsettings->GetScalarBarMode() == vtkPVGeneralSettings::AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS) { - vtkSMColorMapEditorHelper::SetScalarBarVisibility(reprProxy, view, true); + colorMapEditorHelper->SetSelectedScalarBarVisibility(reprProxy, view, true); } } //============================================================================= -class pqDisplayColorWidget::pqInternals +class pqDisplayColorWidget::pqInternals : public QObject { public: pqPropertyLinks Links; - vtkNew Connector; vtkWeakPointer Domain; + + unsigned long DomainObserverId = 0; + int OutOfDomainEntryIndex; QSet NoSolidColor; + vtkNew ColorMapEditorHelper; + pqInternals() : OutOfDomainEntryIndex(-1) { @@ -221,14 +261,13 @@ public: pqDisplayColorWidget::pqDisplayColorWidget(QWidget* parentObject) : Superclass(parentObject) , Internals(new pqDisplayColorWidget::pqInternals()) + , CellDataIcon(new QIcon(":/pqWidgets/Icons/pqCellData.svg")) + , PointDataIcon(new QIcon(":/pqWidgets/Icons/pqPointData.svg")) + , FieldDataIcon(new QIcon(":/pqWidgets/Icons/pqGlobalData.svg")) + , SolidColorIcon(new QIcon(":/pqWidgets/Icons/pqSolidColor.svg")) { - this->CellDataIcon = new QIcon(":/pqWidgets/Icons/pqCellData.svg"); - this->PointDataIcon = new QIcon(":/pqWidgets/Icons/pqPointData.svg"); - this->FieldDataIcon = new QIcon(":/pqWidgets/Icons/pqGlobalData.svg"); - this->SolidColorIcon = new QIcon(":/pqWidgets/Icons/pqSolidColor.svg"); - QHBoxLayout* hbox = new QHBoxLayout(this); - hbox->setContentsMargins(0, 0, 0, 0); + hbox->setContentsMargins(pqPropertiesPanel::suggestedMargins()); hbox->setSpacing(pqPropertiesPanel::suggestedHorizontalSpacing()); this->Variables = new QComboBox(this); @@ -246,28 +285,24 @@ pqDisplayColorWidget::pqDisplayColorWidget(QWidget* parentObject) this->Variables->setEnabled(false); this->Components->setEnabled(false); - this->connect(this->Variables, SIGNAL(currentIndexChanged(int)), SLOT(refreshComponents())); - this->connect(this->Variables, SIGNAL(currentIndexChanged(int)), SLOT(pruneOutOfDomainEntries())); - this->connect(this->Variables, SIGNAL(currentIndexChanged(int)), SIGNAL(arraySelectionChanged())); - this->connect(this->Components, SIGNAL(currentIndexChanged(int)), SLOT(componentNumberChanged())); + QObject::connect(this->Variables, QOverload::of(&QComboBox::currentIndexChanged), [this]() { + this->refreshComponents(); + this->pruneOutOfDomainEntries(); + Q_EMIT this->arraySelectionChanged(); + }); + QObject::connect(this->Components, QOverload::of(&QComboBox::currentIndexChanged), this, + &pqDisplayColorWidget::componentNumberChanged); - this->connect(&this->Internals->Links, SIGNAL(qtWidgetChanged()), SLOT(renderActiveView())); + QObject::connect(&this->Internals->Links, &pqPropertyLinks::qtWidgetChanged, this, + &pqDisplayColorWidget::renderActiveView); - this->connect(this, &pqDisplayColorWidget::representationTextChanged, [this](const QString&) { - // The representation type, like Slice or Volume, can disable some entries. - this->refreshColorArrayNames(); - }); + // The representation type, like Slice or Volume, can disable some entries. + QObject::connect(this, &pqDisplayColorWidget::representationTextChanged, this, + &pqDisplayColorWidget::refreshColorArrayNames); } //----------------------------------------------------------------------------- -pqDisplayColorWidget::~pqDisplayColorWidget() -{ - delete this->FieldDataIcon; - delete this->CellDataIcon; - delete this->PointDataIcon; - delete this->SolidColorIcon; - delete this->Internals; -} +pqDisplayColorWidget::~pqDisplayColorWidget() = default; //----------------------------------------------------------------------------- void pqDisplayColorWidget::renderActiveView() @@ -289,9 +324,10 @@ vtkSMViewProxy* pqDisplayColorWidget::viewProxy() const } //----------------------------------------------------------------------------- -void pqDisplayColorWidget::setRepresentation(pqDataRepresentation* repr) +void pqDisplayColorWidget::setRepresentation(pqDataRepresentation* repr, int selectedPropertiesType) { - if (this->Representation != nullptr && this->Representation == repr) + if (this->Representation != nullptr && this->Representation == repr && + this->Internals->ColorMapEditorHelper->GetSelectedPropertiesType() == selectedPropertiesType) { return; } @@ -311,7 +347,11 @@ void pqDisplayColorWidget::setRepresentation(pqDataRepresentation* repr) this->Components->setEnabled(false); this->Internals->Links.clear(); this->Internals->NoSolidColor.clear(); - this->Internals->Connector->Disconnect(); + if (this->Internals->DomainObserverId != 0 && this->Internals->Domain) + { + this->Internals->Domain->GetProperty()->RemoveObserver(this->Internals->DomainObserverId); + this->Internals->DomainObserverId = 0; + } this->Internals->Domain = nullptr; this->Variables->clear(); this->Components->clear(); @@ -319,34 +359,48 @@ void pqDisplayColorWidget::setRepresentation(pqDataRepresentation* repr) // now, save the new repr. this->Representation = repr; + this->Internals->ColorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); vtkSMProxy* proxy = repr ? repr->getProxy() : nullptr; - bool can_color = (proxy != nullptr && proxy->GetProperty("ColorArrayName") != nullptr); - if (!can_color) + vtkSMProperty* colorArrayProp = + proxy ? this->Internals->ColorMapEditorHelper->GetSelectedColorArrayProperty(proxy) : nullptr; + const bool canColor = (proxy != nullptr && colorArrayProp != nullptr); + if (!canColor) { return; } - vtkSMProperty* prop = proxy->GetProperty("ColorArrayName"); - auto domain = prop->FindDomain(); + auto domain = colorArrayProp->FindDomain(); if (!domain) { - qCritical("Representation has ColorArrayName property without " - "a vtkSMArrayListDomain. This is no longer supported."); + qCritical("%s", + QString("Representation has %1 property without " + "a vtkSMArrayListDomain. This is no longer supported.") + .arg(colorArrayProp->GetXMLName()) + .toUtf8() + .data()); return; } this->Internals->Domain = domain; - - this->Internals->Connector->Connect( - prop, vtkCommand::DomainModifiedEvent, this, SLOT(refreshColorArrayNames())); + this->Internals->DomainObserverId = pqCoreUtilities::connect( + colorArrayProp, vtkCommand::DomainModifiedEvent, this, SLOT(refreshColorArrayNames())); this->refreshColorArrayNames(); - // monitor LUT changes. we need to link the component number on the LUT's - // property. - this->connect(repr, SIGNAL(colorTransferFunctionModified()), SLOT(updateColorTransferFunction())); + // monitor LUT changes. we need to link the component number on the LUT's property. + if (this->Internals->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks) + { + QObject::connect(repr, &pqDataRepresentation::blockColorTransferFunctionModified, this, + &pqDisplayColorWidget::updateColorTransferFunction); + } + else + { + QObject::connect(repr, &pqDataRepresentation::colorTransferFunctionModified, this, + &pqDisplayColorWidget::updateColorTransferFunction); + } this->Internals->Links.addPropertyLink( - this, "notused", SIGNAL(arraySelectionChanged()), proxy, prop); + this, "notused", SIGNAL(arraySelectionChanged()), proxy, colorArrayProp); // add a link to representation, because some representations don't like Solid Color vtkSMProperty* reprProperty = proxy->GetProperty("Representation"); if (reprProperty) @@ -390,7 +444,7 @@ void pqDisplayColorWidget::setArraySelection(const QPair& value) association = vtkDataObject::POINT; } - QVariant idata = this->itemData(association, arrayName); + const QVariant idata = this->itemData(association, arrayName); int index = this->Variables->findData(idata); if (index == -1) { @@ -399,7 +453,7 @@ void pqDisplayColorWidget::setArraySelection(const QPair& value) // domain) that the selected array is not present in the domain. In that // case, based on what we know of the array selection, we update the UI and // add that entry to the UI. - bool prev = this->Variables->blockSignals(true); + const bool prev = this->Variables->blockSignals(true); index = this->addOutOfDomainEntry(association, arrayName); this->Variables->blockSignals(prev); } @@ -417,22 +471,22 @@ QVariant pqDisplayColorWidget::itemData(int association, const QString& arrayNam //----------------------------------------------------------------------------- QIcon* pqDisplayColorWidget::itemIcon(int association, const QString& arrayName) const { - QVariant idata = this->itemData(association, arrayName); + const QVariant idata = this->itemData(association, arrayName); if (!idata.isValid()) { - return this->SolidColorIcon; + return this->SolidColorIcon.get(); } if (association == vtkDataObject::FIELD) { - return this->FieldDataIcon; + return this->FieldDataIcon.get(); } else if (association == vtkDataObject::CELL) { - return this->CellDataIcon; + return this->CellDataIcon.get(); } else { - return this->PointDataIcon; + return this->PointDataIcon.get(); } } @@ -441,9 +495,10 @@ void pqDisplayColorWidget::refreshColorArrayNames() { // Simply update the this->Variables combo-box with values from the domain. // Try to preserve the current text if possible. - QVariant current = pqDisplayColorWidget::PropertyLinksConnection::convert(this->arraySelection()); + const QVariant current = + pqDisplayColorWidget::PropertyLinksConnection::convert(this->arraySelection()); - bool prev = this->Variables->blockSignals(true); + const bool prev = this->Variables->blockSignals(true); this->Variables->clear(); this->Internals->OutOfDomainEntryIndex = -1; @@ -452,44 +507,29 @@ void pqDisplayColorWidget::refreshColorArrayNames() this->Variables->addItem(*this->SolidColorIcon, tr("Solid Color"), QVariant()); if (this->Representation && this->Internals->NoSolidColor.contains(this->representationText())) { - QStandardItemModel* model = qobject_cast(this->Variables->model()); + const auto model = qobject_cast(this->Variables->model()); QStandardItem* item = model->item(0); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } vtkSMArrayListDomain* domain = this->Internals->Domain; assert(domain); - - QSet uniquifier; // we sometimes end up with duplicate entries. - // overcome that for now. We need a proper fix in - // vtkSMArrayListDomain/vtkSMRepresentedArrayListDomain/vtkPVDataInformation. - // for now, just do this. To reproduce the problem, skip the uniquifier check, - // and the load can.ex2, uncheck all blocks, and check all sets. The color-by - // combo will have duplicated ObjectId, SourceElementId, and SourceElementSide entries. for (unsigned int cc = 0, max = domain->GetNumberOfStrings(); cc < max; cc++) { - int icon_association = domain->GetDomainAssociation(cc); - int association = domain->GetFieldAssociation(cc); - QString name = domain->GetString(cc); - QString label = name; - if (domain->IsArrayPartial(cc)) - { - label += " (partial)"; - } - QIcon* icon = this->itemIcon(icon_association, name); - QVariant idata = this->itemData(association, name); - QString key = QString("%1.%2").arg(association).arg(name); - if (!uniquifier.contains(key)) - { - this->Variables->addItem(*icon, label, idata); - uniquifier.insert(key); - } + const int icon_association = domain->GetDomainAssociation(cc); + const int association = domain->GetFieldAssociation(cc); + const QString name = domain->GetString(cc); + const QString label = !domain->IsArrayPartial(cc) ? name : QString("%1 (partial)").arg(name); + const QIcon* icon = this->itemIcon(icon_association, name); + const QVariant idata = this->itemData(association, name); + const QString key = QString("%1.%2").arg(association).arg(name); + this->Variables->addItem(*icon, label, idata); } // doing this here, instead of in the constructor ensures that the - // popup menu shows resonably on OsX. + // popup menu shows reasonably on OsX. this->Variables->setMaxVisibleItems(this->Variables->count() + 1); - int idx = this->Variables->findData(current); + const int idx = this->Variables->findData(current); if (idx > 0) { this->Variables->setCurrentIndex(idx); @@ -499,7 +539,7 @@ void pqDisplayColorWidget::refreshColorArrayNames() // The previous value set on the widget is no longer available in the // "domain". This happens when the pipeline is modified, for example. In // that case, the UI still needs to reflect the value. So we add it. - QPair currentColoring = + const QPair currentColoring = pqDisplayColorWidget::PropertyLinksConnection::convert(current); this->Variables->setCurrentIndex( this->addOutOfDomainEntry(currentColoring.first, currentColoring.second)); @@ -524,7 +564,7 @@ int pqDisplayColorWidget::componentNumber() const // here, we treat -1 as the magnitude (to be consistent with // VTK/vtkScalarsToColors). This mismatches with how // vtkPVArrayInformation refers to magnitude. - int index = this->Components->currentIndex(); + const int index = this->Components->currentIndex(); return index >= 0 ? this->Components->itemData(index).toInt() : -1; } @@ -555,33 +595,59 @@ void pqDisplayColorWidget::componentNumberChanged() { if (this->ColorTransferFunction) { - BEGIN_UNDO_SET(tr("Change color component")); - int number = this->componentNumber(); - QPair val = this->arraySelection(); - int association = val.first; + BEGIN_UNDO_SET(tr("Change Color Component")); + const int number = this->componentNumber(); + const QPair val = this->arraySelection(); + const int association = val.first; const QString& arrayName = val.second; - this->ColorTransferFunction->setVectorMode( - number < 0 ? pqScalarsToColors::MAGNITUDE : pqScalarsToColors::COMPONENT, - number < 0 ? 0 : number); - { // To make sure the trace is done before other calls. + + const int vectorMode = number < 0 ? pqScalarsToColors::MAGNITUDE : pqScalarsToColors::COMPONENT; + const int vectorComponent = + vectorMode == pqScalarsToColors::COMPONENT ? std::max(number, 0) : 0; + + vtkSMProxy* repr = this->Representation ? this->Representation->getProxy() : nullptr; + + const std::vector luts = + this->Internals->ColorMapEditorHelper->GetSelectedLookupTables(repr); + for (const auto& lut : luts) + { + // pqScalarsToColors::setVectorMode + vtkSMPropertyHelper(lut, "VectorMode").Set(vectorMode); + vtkSMPropertyHelper(lut, "VectorComponent").Set(vectorComponent); + lut->UpdateVTKObjects(); + } + + if (this->Internals->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Representation) + { SM_SCOPED_TRACE(SetScalarColoring) - .arg("display", this->Representation->getProxy()) + .arg("display", repr) .arg("arrayname", arrayName.toUtf8().data()) .arg("attribute_type", association) .arg("component", this->Components->itemText(number + 1).toUtf8().data()) - .arg("lut", this->ColorTransferFunction->getProxy()); + .arg("lut", luts.front()); } - - // we could now respect some application setting to determine if the LUT is - // to be reset. - vtkSMProxy* reprProxy = this->Representation ? this->Representation->getProxy() : nullptr; - vtkSMColorMapEditorHelper::RescaleTransferFunctionToDataRange( - reprProxy, /*extend*/ false, /*force*/ false); + else + { + SM_SCOPED_TRACE(SetBlocksScalarColoring) + .arg("display", repr) + .arg( + "block_selectors", this->Internals->ColorMapEditorHelper->GetSelectedBlockSelectors(repr)) + .arg("arrayname", arrayName.toUtf8().data()) + .arg("attribute_type", association) + .arg("component", this->Components->itemText(number + 1).toUtf8().data()) + .arg("luts", std::vector({ luts.begin(), luts.end() })); + } + // we could now respect some application setting to determine if the LUT is to be reset. + this->Internals->ColorMapEditorHelper->RescaleSelectedTransferFunctionToDataRange( + repr, /*extend*/ false, /*force*/ false); // Update scalar bars. vtkNew tmgr; - tmgr->UpdateScalarBarsComponentTitle(this->ColorTransferFunction->getProxy(), reprProxy); - + for (const auto& lut : luts) + { + tmgr->UpdateScalarBarsComponentTitle(lut, repr); + } END_UNDO_SET(); // render all views since this could affect multiple views. @@ -597,10 +663,9 @@ void pqDisplayColorWidget::refreshComponents() return; } - int comp_number = this->componentNumber(); - - QPair val = this->arraySelection(); - int association = val.first; + const int compNumber = this->componentNumber(); + const QPair val = this->arraySelection(); + const int association = val.first; const QString& arrayName = val.second; vtkPVDataInformation* dataInfo = this->Representation->getInputDataInformation(); @@ -617,19 +682,19 @@ void pqDisplayColorWidget::refreshComponents() { // using solid color or coloring by non-existing array. this->Components->setEnabled(false); - bool prev = this->Components->blockSignals(true); + const bool prev = this->Components->blockSignals(true); this->Components->clear(); this->Components->blockSignals(prev); return; } - bool prev = this->Components->blockSignals(true); + const bool prev = this->Components->blockSignals(true); const int nComponents = arrayInfo->GetNumberOfComponents(); this->Components->clear(); // add magnitude for non-scalar values when needed - auto* arraySettings = vtkPVRepresentedArrayListSettings::GetInstance(); + auto arraySettings = vtkPVRepresentedArrayListSettings::GetInstance(); if (nComponents > 1 && arraySettings->ShouldUseMagnitudeMode(nComponents)) { this->Components->addItem(arrayInfo->GetComponentName(-1), -1); @@ -641,7 +706,7 @@ void pqDisplayColorWidget::refreshComponents() } /// restore component choice if possible. - int idx = this->Components->findData(comp_number); + const int idx = this->Components->findData(compNumber); if (idx != -1) { this->Components->setCurrentIndex(idx); @@ -658,19 +723,21 @@ void pqDisplayColorWidget::updateColorTransferFunction() return; } - pqScalarsToColors* lut = this->Representation->getLookupTable(); + pqScalarsToColors* lut = this->Representation->getLookupTable( + this->Internals->ColorMapEditorHelper->GetSelectedPropertiesType()); if (this->ColorTransferFunction && this->ColorTransferFunction != lut) { this->disconnect(this->ColorTransferFunction); this->ColorTransferFunction->disconnect(this); + this->ColorTransferFunction = nullptr; } - this->ColorTransferFunction = lut; if (lut) { + this->ColorTransferFunction = lut; this->setComponentNumber( lut->getVectorMode() == pqScalarsToColors::MAGNITUDE ? -1 : lut->getVectorComponent()); - this->connect(lut, SIGNAL(componentOrModeChanged()), SLOT(updateColorTransferFunction()), - Qt::UniqueConnection); + QObject::connect(lut, &pqScalarsToColors::componentOrModeChanged, this, + &pqDisplayColorWidget::updateColorTransferFunction, Qt::UniqueConnection); } } diff --git a/Qt/Components/pqDisplayColorWidget.h b/Qt/Components/pqDisplayColorWidget.h index 2afeebeec68..7de30ad5ba0 100644 --- a/Qt/Components/pqDisplayColorWidget.h +++ b/Qt/Components/pqDisplayColorWidget.h @@ -6,14 +6,17 @@ #include "pqComponentsModule.h" +#include #include #include +#include #include +#include + +class QComboBox; class pqDataRepresentation; class pqScalarsToColors; -class QComboBox; -class vtkEventQtSlotConnect; class vtkSMProxy; class vtkSMViewProxy; @@ -53,11 +56,17 @@ public: */ vtkSMViewProxy* viewProxy() const; + /** + * Hides the scalar bars if not needed. + */ + static void hideScalarBarsIfNotNeeded(vtkSMViewProxy* view, std::vector luts); + /** * Updates the scalar bar visibility of the representation `reprProxy` in `view`. * The behavior of the scalar bar visibility is dependent of the general settings. */ - static void updateScalarBarVisibility(vtkSMViewProxy* view, vtkSMProxy* reprProxy); + static void updateScalarBarVisibility( + vtkSMViewProxy* view, vtkSMProxy* reprProxy, int selectedPropertiesType = 0 /*Representation*/); /** * Returns the selected representation type as a string. @@ -76,10 +85,16 @@ Q_SIGNALS: void representationTextChanged(const QString& text); public Q_SLOTS: + ///@{ /** * Set the representation to control the scalar coloring properties on. */ - void setRepresentation(pqDataRepresentation* display); + void setRepresentation(pqDataRepresentation* display, int selectedPropertiesType); + void setRepresentation(pqDataRepresentation* display) + { + this->setRepresentation(display, 0 /*Representation*/); + } + ///@} /** * set representation type. @@ -147,10 +162,10 @@ private: */ int addOutOfDomainEntry(int association, const QString& arrayName); - QIcon* CellDataIcon; - QIcon* PointDataIcon; - QIcon* FieldDataIcon; - QIcon* SolidColorIcon; + QScopedPointer CellDataIcon; + QScopedPointer PointDataIcon; + QScopedPointer FieldDataIcon; + QScopedPointer SolidColorIcon; QComboBox* Variables; QComboBox* Components; QPointer Representation; @@ -158,7 +173,7 @@ private: QString RepresentationText; class pqInternals; - pqInternals* Internals; + QScopedPointer Internals; class PropertyLinksConnection; friend class PropertyLinksConnection; diff --git a/Qt/Components/pqDisplayRepresentationWidget.cxx b/Qt/Components/pqDisplayRepresentationWidget.cxx index 4d9bc9958a0..cb1bce9369a 100644 --- a/Qt/Components/pqDisplayRepresentationWidget.cxx +++ b/Qt/Components/pqDisplayRepresentationWidget.cxx @@ -13,14 +13,12 @@ #include "vtkPVXMLElement.h" #include "vtkSMColorMapEditorHelper.h" #include "vtkSMPVRepresentationProxy.h" -#include "vtkSMRepresentationProxy.h" #include "vtkSMViewProxy.h" #include #include #include -#include //============================================================================= class pqDisplayRepresentationWidget::PropertyLinksConnection : public pqPropertyLinksConnection @@ -67,7 +65,8 @@ protected: // because volume redering can only be done when using a scalar field. if (!lutProxy && type == QString("Volume")) { - pqDisplayColorWidget::updateScalarBarVisibility(view, reprProxy); + pqDisplayColorWidget::updateScalarBarVisibility( + view, reprProxy, vtkSMColorMapEditorHelper::Representation); } } END_UNDO_SET(); -- GitLab From bdf5ad6f04ab92aa71efb79a853708d32351d171 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 14:20:03 -0400 Subject: [PATCH 20/43] pqMultiBlockPropertiesStateWidget: Implementation This widget will be used by: 1. pqMultiBlockPropertiesEditorWidget 2. pqColorEditorPropertyWidget 3. pqDataAssemblyPropertyWidget --- Qt/Components/CMakeLists.txt | 1 + .../Resources/Icons/pqStateBlockInherited.svg | 28 ++ .../Resources/Icons/pqStateDisabled.svg | 14 + .../Resources/Icons/pqStateMixedInherited.svg | 32 ++ .../Icons/pqStateRepresentationInherited.svg | 27 ++ Qt/Components/Resources/Icons/pqStateSet.svg | 27 ++ .../Icons/pqStateSetAndBlockInherited.svg | 32 ++ .../Icons/pqStateSetAndMixedInherited.svg | 32 ++ .../pqStateSetAndRepresentationInherited.svg | 32 ++ Qt/Components/Resources/pqComponents.qrc | 8 + .../pqMultiBlockPropertiesStateWidget.cxx | 360 ++++++++++++++++++ .../pqMultiBlockPropertiesStateWidget.h | 94 +++++ 12 files changed, 687 insertions(+) create mode 100644 Qt/Components/Resources/Icons/pqStateBlockInherited.svg create mode 100644 Qt/Components/Resources/Icons/pqStateDisabled.svg create mode 100644 Qt/Components/Resources/Icons/pqStateMixedInherited.svg create mode 100644 Qt/Components/Resources/Icons/pqStateRepresentationInherited.svg create mode 100644 Qt/Components/Resources/Icons/pqStateSet.svg create mode 100644 Qt/Components/Resources/Icons/pqStateSetAndBlockInherited.svg create mode 100644 Qt/Components/Resources/Icons/pqStateSetAndMixedInherited.svg create mode 100644 Qt/Components/Resources/Icons/pqStateSetAndRepresentationInherited.svg create mode 100644 Qt/Components/pqMultiBlockPropertiesStateWidget.cxx create mode 100644 Qt/Components/pqMultiBlockPropertiesStateWidget.h diff --git a/Qt/Components/CMakeLists.txt b/Qt/Components/CMakeLists.txt index 73d992bc56a..d47737b2a8b 100644 --- a/Qt/Components/CMakeLists.txt +++ b/Qt/Components/CMakeLists.txt @@ -88,6 +88,7 @@ set(classes pqLogViewerDialog pqMemoryInspectorPanel pqMultiBlockInspectorWidget + pqMultiBlockPropertiesStateWidget pqMultiViewWidget pqOneLinerTextEdit pqOrbitCreatorDialog diff --git a/Qt/Components/Resources/Icons/pqStateBlockInherited.svg b/Qt/Components/Resources/Icons/pqStateBlockInherited.svg new file mode 100644 index 00000000000..72fce0c65fa --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateBlockInherited.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/Qt/Components/Resources/Icons/pqStateDisabled.svg b/Qt/Components/Resources/Icons/pqStateDisabled.svg new file mode 100644 index 00000000000..20c19d43982 --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateDisabled.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/Qt/Components/Resources/Icons/pqStateMixedInherited.svg b/Qt/Components/Resources/Icons/pqStateMixedInherited.svg new file mode 100644 index 00000000000..dbfdce7e70f --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateMixedInherited.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/Qt/Components/Resources/Icons/pqStateRepresentationInherited.svg b/Qt/Components/Resources/Icons/pqStateRepresentationInherited.svg new file mode 100644 index 00000000000..75a6d94505e --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateRepresentationInherited.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/Qt/Components/Resources/Icons/pqStateSet.svg b/Qt/Components/Resources/Icons/pqStateSet.svg new file mode 100644 index 00000000000..55c0fa949ac --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateSet.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/Qt/Components/Resources/Icons/pqStateSetAndBlockInherited.svg b/Qt/Components/Resources/Icons/pqStateSetAndBlockInherited.svg new file mode 100644 index 00000000000..e6c4fd933ee --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateSetAndBlockInherited.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/Qt/Components/Resources/Icons/pqStateSetAndMixedInherited.svg b/Qt/Components/Resources/Icons/pqStateSetAndMixedInherited.svg new file mode 100644 index 00000000000..f4ec0635b82 --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateSetAndMixedInherited.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/Qt/Components/Resources/Icons/pqStateSetAndRepresentationInherited.svg b/Qt/Components/Resources/Icons/pqStateSetAndRepresentationInherited.svg new file mode 100644 index 00000000000..a3d083fa45e --- /dev/null +++ b/Qt/Components/Resources/Icons/pqStateSetAndRepresentationInherited.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/Qt/Components/Resources/pqComponents.qrc b/Qt/Components/Resources/pqComponents.qrc index c1fa75a7341..4ed7fd58629 100644 --- a/Qt/Components/Resources/pqComponents.qrc +++ b/Qt/Components/Resources/pqComponents.qrc @@ -198,6 +198,14 @@ Icons/pqSplitHorizontal.svg Icons/pqSplitVertical.svg Icons/pqSpreadsheet.svg + Icons/pqStateBlockInherited.svg + Icons/pqStateDisabled.svg + Icons/pqStateMixedInherited.svg + Icons/pqStateRepresentationInherited.svg + Icons/pqStateSet.svg + Icons/pqStateSetAndBlockInherited.svg + Icons/pqStateSetAndMixedInherited.svg + Icons/pqStateSetAndRepresentationInherited.svg Icons/pqStep24.png Icons/pqStreamTracer.svg Icons/pqStructuredGrid16.png diff --git a/Qt/Components/pqMultiBlockPropertiesStateWidget.cxx b/Qt/Components/pqMultiBlockPropertiesStateWidget.cxx new file mode 100644 index 00000000000..04b5fad6182 --- /dev/null +++ b/Qt/Components/pqMultiBlockPropertiesStateWidget.cxx @@ -0,0 +1,360 @@ +// SPDX-FileCopyrightText: Copyright (c) Kitware Inc. +// SPDX-License-Identifier: BSD-3-Clause +#include "pqMultiBlockPropertiesStateWidget.h" + +#include "pqCoreUtilities.h" +#include "pqHighlightableToolButton.h" +#include "pqPropertiesPanel.h" +#include "pqUndoStack.h" + +#include "vtkCommand.h" +#include "vtkNew.h" +#include "vtkSMColorMapEditorHelper.h" +#include "vtkSMProperty.h" +#include "vtkSMProxy.h" +#include "vtkSMStringVectorProperty.h" +#include "vtkWeakPointer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//----------------------------------------------------------------------------- +class pqMultiBlockPropertiesStateWidget::pqInternals : public QObject +{ +public: + pqInternals() { this->ColorMapEditorHelper->SetSelectedPropertiesTypeToBlocks(); } + ~pqInternals() override = default; + + vtkWeakPointer Representation; + std::vector BlockPropertyNames; + int IconSize; + QString Selector; + BlockPropertyState CurrentState; + + QPointer HLayout; + QPointer StateLabel; + QPointer ResetButton; + QPixmap Icons[BlockPropertyState::NumberOfStates]; + QString ToolTips[BlockPropertyState::NumberOfStates]; + + vtkNew ColorMapEditorHelper; + + /** + * The set/blockInherited/representationInherited colors were selected from the IBM Design library + * palette. See https://davidmathlogic.com/colorblind. + */ + static QPixmap statePixMap(vtkSMColorMapEditorHelper::BlockPropertyState state, int iconSize, + QColor setColor = QColor("#D91D86") /*magenta*/, + QColor blockInheritedColor = QColor("#F6BB00") /*yellow*/, + QColor representationInheritedColor = QColor("#3665B7"), /*blue*/ + QColor penColor = QColor(Qt::black), QColor backgroundColor = QColor(Qt::white)) + { + QPixmap pixmap(iconSize, iconSize); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + painter.setRenderHint(QPainter::Antialiasing, true); + QPen pen = painter.pen(); + QBrush brush = painter.brush(); + + // Check each state individually and draw corresponding shapes + const double circumCircleRadius = iconSize / 2.1; + const double circumCircleRadius3_4 = circumCircleRadius * 3.0 / 4.0; + const double circumCircleRadius1_2 = circumCircleRadius / 2.0; + const QPointF center(iconSize / 2.0, iconSize / 2.0); + if (state == vtkSMColorMapEditorHelper::BlockPropertyState::Disabled) + { + pen.setWidth(iconSize / 20); + pen.setColor(penColor); + pen.setStyle(Qt::SolidLine); + painter.setPen(pen); + painter.setBrush(Qt::NoBrush); + // Draw a circle + painter.drawEllipse(center, circumCircleRadius, circumCircleRadius); + return pixmap; + } + else // (state >= vtkSMColorMapEditorHelper::BlockPropertyState::RepresentationInherited) + { + pen.setWidth(iconSize / 20); + pen.setColor(penColor); + pen.setStyle(Qt::SolidLine); + painter.setPen(pen); + painter.setBrush(backgroundColor); + // Draw a circle + painter.drawEllipse(center, circumCircleRadius, circumCircleRadius); + } + if (state & vtkSMColorMapEditorHelper::BlockPropertyState::RepresentationInherited) + { + painter.setPen(Qt::NoPen); + // Draw an annulus + painter.setBrush(representationInheritedColor); + painter.drawEllipse(center, circumCircleRadius, circumCircleRadius); + painter.setBrush(backgroundColor); + painter.drawEllipse(center, circumCircleRadius3_4, circumCircleRadius3_4); + } + if (state & vtkSMColorMapEditorHelper::BlockPropertyState::BlockInherited) + { + painter.setPen(Qt::NoPen); + // Draw an annulus + painter.setBrush(blockInheritedColor); + painter.drawEllipse(center, circumCircleRadius3_4, circumCircleRadius3_4); + painter.setBrush(backgroundColor); + painter.drawEllipse(center, circumCircleRadius1_2, circumCircleRadius1_2); + } + if (state & vtkSMColorMapEditorHelper::BlockPropertyState::Set) + { + painter.setPen(Qt::NoPen); + // Draw a circle + painter.setBrush(setColor); + painter.drawEllipse(center, circumCircleRadius1_2, circumCircleRadius1_2); + } + return pixmap; + } +}; + +//----------------------------------------------------------------------------- +pqMultiBlockPropertiesStateWidget::pqMultiBlockPropertiesStateWidget(vtkSMProxy* repr, + const QList& propertyNames, int iconSize, QString selector, QWidget* parent, + Qt::WindowFlags f) + : Superclass(parent, f) + , Internals(new pqMultiBlockPropertiesStateWidget::pqInternals()) +{ + auto& internals = *this->Internals; + internals.Representation = repr; + internals.IconSize = iconSize; + internals.CurrentState = BlockPropertyState::NumberOfStates; // invalid state + internals.Selector = selector; + + // populate the properties + for (const auto& propertyName : propertyNames) + { + const std::string propName = propertyName.toStdString(); + auto prop = vtkSMStringVectorProperty::SafeDownCast(repr->GetProperty(propName.c_str())); + if (vtkSMColorMapEditorHelper::GetPropertyType(prop) == vtkSMColorMapEditorHelper::Blocks) + { + internals.BlockPropertyNames.push_back(propName); + } + } + + // create the icons + // for (int state = 0; state < BlockPropertyState::NumberOfStates; ++state) + // { + // QPixmap pixmap = internals.statePixMap(static_cast(state), 512); + // QString fileName = QFileDialog::getSaveFileName(this, "Save Image", "", "PNG Files (*.png)"); + // pixmap.save(fileName, "PNG"); + // } + // load the icons + internals.Icons[BlockPropertyState::Disabled] = + QIcon(":/pqWidgets/Icons/pqStateDisabled.svg").pixmap(internals.IconSize, internals.IconSize); + internals.Icons[BlockPropertyState::RepresentationInherited] = + QIcon(":/pqWidgets/Icons/pqStateRepresentationInherited.svg") + .pixmap(internals.IconSize, internals.IconSize); + internals.Icons[BlockPropertyState::BlockInherited] = + QIcon(":/pqWidgets/Icons/pqStateBlockInherited.svg") + .pixmap(internals.IconSize, internals.IconSize); + internals.Icons[BlockPropertyState::MixedInherited] = + QIcon(":/pqWidgets/Icons/pqStateMixedInherited.svg") + .pixmap(internals.IconSize, internals.IconSize); + internals.Icons[BlockPropertyState::Set] = + QIcon(":/pqWidgets/Icons/pqStateSet.svg").pixmap(internals.IconSize, internals.IconSize); + internals.Icons[BlockPropertyState::SetAndRepresentationInherited] = + QIcon(":/pqWidgets/Icons/pqStateSetAndRepresentationInherited.svg") + .pixmap(internals.IconSize, internals.IconSize); + internals.Icons[BlockPropertyState::SetAndBlockInherited] = + QIcon(":/pqWidgets/Icons/pqStateSetAndBlockInherited.svg") + .pixmap(internals.IconSize, internals.IconSize); + internals.Icons[BlockPropertyState::SetAndMixedInherited] = + QIcon(":/pqWidgets/Icons/pqStateSetAndMixedInherited.svg") + .pixmap(internals.IconSize, internals.IconSize); + // create the tooltips + const QString toolTipFirstPart = + internals.BlockPropertyNames.size() > 1 ? tr("Properties are ") : tr("Property is "); + const QString toolTipSecondPart = + internals.BlockPropertyNames.size() > 1 ? tr(" they ") : tr(" it "); + internals.ToolTips[BlockPropertyState::Disabled] = + toolTipFirstPart + tr("disabled because") + toolTipSecondPart + tr("can not be edited"); + internals.ToolTips[BlockPropertyState::RepresentationInherited] = + toolTipFirstPart + tr("inherited from the representation"); + internals.ToolTips[BlockPropertyState::BlockInherited] = + toolTipFirstPart + tr("inherited from block(s)"); + internals.ToolTips[BlockPropertyState::MixedInherited] = + toolTipFirstPart + tr("inherited from block(s) and the representation"); + internals.ToolTips[BlockPropertyState::Set] = toolTipFirstPart + tr("set in block(s)"); + internals.ToolTips[BlockPropertyState::SetAndRepresentationInherited] = + toolTipFirstPart + tr("set in block(s) and inherited from the representation"); + internals.ToolTips[BlockPropertyState::SetAndBlockInherited] = + toolTipFirstPart + tr("set in block(s) and inherited from block(s)"); + internals.ToolTips[BlockPropertyState::SetAndMixedInherited] = + toolTipFirstPart + tr("set in block(s) and inherited from block(s) and the representation"); + + // create the layout + internals.HLayout = new QHBoxLayout(this); + internals.HLayout->setSpacing(pqPropertiesPanel::suggestedHorizontalSpacing()); + internals.HLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + internals.HLayout->setContentsMargins(pqPropertiesPanel::suggestedMargin(), + pqPropertiesPanel::suggestedMargin(), pqPropertiesPanel::suggestedMargin(), + pqPropertiesPanel::suggestedMargin()); + internals.HLayout->setStretch(0, 1); + + // create the reset button + internals.ResetButton = new pqHighlightableToolButton(this); + internals.ResetButton->setObjectName("Reset"); + internals.ResetButton->setIcon(QIcon(":/pqWidgets/Icons/pqReset.svg")); + internals.ResetButton->setIconSize(QSize(iconSize - 4, iconSize - 4)); + internals.ResetButton->setToolTip(tr("Reset to value(s) inherited from block/representation")); + + // create the state label + internals.StateLabel = new QLabel("BlockPropertyState", this); + internals.StateLabel->setToolTip(internals.ToolTips[BlockPropertyState::Disabled]); + internals.StateLabel->setPixmap(internals.Icons[BlockPropertyState::Disabled]); + + // add the widgets to the layout + internals.HLayout->addWidget(internals.StateLabel); + internals.HLayout->addWidget(internals.ResetButton); + + // set the initial state + this->setState(this->Internals->Selector.isEmpty() ? BlockPropertyState::Disabled + : BlockPropertyState::RepresentationInherited); + + // connect the signals + for (const auto& propertyName : internals.BlockPropertyNames) + { + pqCoreUtilities::connect(repr->GetProperty(propertyName.c_str()), vtkCommand::ModifiedEvent, + this, SLOT(onPropertiesChanged())); + } + if (this->Internals->Selector.isEmpty()) + { + if (vtkSMProperty* selectedBlockSelectors = repr->GetProperty("SelectedBlockSelectors")) + { + pqCoreUtilities::connect(selectedBlockSelectors, vtkCommand::ModifiedEvent, this, + SLOT(onSelectedBlockSelectorsChanged())); + } + } + QObject::connect(internals.ResetButton, &pqHighlightableToolButton::clicked, this, + &pqMultiBlockPropertiesStateWidget::onResetButtonClicked); +} + +//----------------------------------------------------------------------------- +pqMultiBlockPropertiesStateWidget::~pqMultiBlockPropertiesStateWidget() = default; + +//----------------------------------------------------------------------------- +void pqMultiBlockPropertiesStateWidget::setState(BlockPropertyState state) +{ + auto& internals = *this->Internals; + if (internals.CurrentState != state) + { + internals.CurrentState = state; + + internals.StateLabel->setPixmap(internals.Icons[state]); + internals.StateLabel->setToolTip(internals.ToolTips[state]); + internals.StateLabel->setEnabled(state != BlockPropertyState::Disabled); + + internals.ResetButton->setEnabled(state >= BlockPropertyState::Set); + + Q_EMIT this->stateChanged(state); + } +} + +//----------------------------------------------------------------------------- +pqMultiBlockPropertiesStateWidget::BlockPropertyState pqMultiBlockPropertiesStateWidget::getState() + const +{ + return this->Internals->CurrentState; +} + +//----------------------------------------------------------------------------- +QPixmap pqMultiBlockPropertiesStateWidget::getStatePixmap(BlockPropertyState state) const +{ + return this->Internals->Icons[state]; +} + +//----------------------------------------------------------------------------- +QString pqMultiBlockPropertiesStateWidget::getStateToolTip(BlockPropertyState state) const +{ + return this->Internals->ToolTips[state]; +} + +//----------------------------------------------------------------------------- +pqHighlightableToolButton* pqMultiBlockPropertiesStateWidget::getResetButton() const +{ + return this->Internals->ResetButton; +} + +//----------------------------------------------------------------------------- +std::vector pqMultiBlockPropertiesStateWidget::getProperties() const +{ + return this->Internals->BlockPropertyNames; +} + +//----------------------------------------------------------------------------- +std::vector pqMultiBlockPropertiesStateWidget::getSelectors() const +{ + if (!this->Internals->Selector.isEmpty()) + { + return { this->Internals->Selector.toStdString() }; + } + else + { + return this->Internals->ColorMapEditorHelper->GetSelectedBlockSelectors( + this->Internals->Representation); + } +} + +//----------------------------------------------------------------------------- +void pqMultiBlockPropertiesStateWidget::onPropertiesChanged() +{ + auto& internals = *this->Internals; + const std::vector blockSelectors = this->getSelectors(); + const std::pair selectorAndState = + internals.ColorMapEditorHelper->HasBlocksProperties( + internals.Representation, blockSelectors, internals.BlockPropertyNames); + this->setState(selectorAndState.second); +} + +//----------------------------------------------------------------------------- +void pqMultiBlockPropertiesStateWidget::onSelectedBlockSelectorsChanged() +{ + const BlockPropertyState oldState = this->getState(); + this->onPropertiesChanged(); + // if the state remains the same, we still need to emit a signal, + // because the selected block selectors have changed + if (oldState == this->getState()) + { + Q_EMIT this->selectedBlockSelectorsChanged(); + } +} + +//----------------------------------------------------------------------------- +void pqMultiBlockPropertiesStateWidget::onResetButtonClicked() +{ + Q_EMIT this->startStateReset(); + auto& internals = *this->Internals; + auto selectedBlockSelectors = this->getSelectors(); + for (const auto& propertyName : internals.BlockPropertyNames) + { + const QString undoText = tr("Reset ") + + QCoreApplication::translate("ServerManagerXML", + internals.Representation->GetProperty(propertyName.c_str())->GetXMLLabel()); + BEGIN_UNDO_SET(undoText); + internals.ColorMapEditorHelper->ResetBlocksProperty( + internals.Representation, selectedBlockSelectors, propertyName); + // onPropertiesChanged will be invoked because the property is modified + END_UNDO_SET(); + } + Q_EMIT this->endStateReset(); +} diff --git a/Qt/Components/pqMultiBlockPropertiesStateWidget.h b/Qt/Components/pqMultiBlockPropertiesStateWidget.h new file mode 100644 index 00000000000..941eaaa69a2 --- /dev/null +++ b/Qt/Components/pqMultiBlockPropertiesStateWidget.h @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: Copyright (c) Kitware Inc. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef pqMultiBlockPropertiesStateWidget_h +#define pqMultiBlockPropertiesStateWidget_h + +#include "pqComponentsModule.h" + +#include "vtkSMColorMapEditorHelper.h" + +#include +#include +#include +#include + +#include +#include + +class pqHighlightableToolButton; +class vtkSMProxy; + +/** + * @class pqMultiBlockPropertiesStateWidget + * @brief A QWidget that tracks the state multi block property/ies, and allows to reset them. + * + * This widget is used to track the state of multiple properties of a multi block proxy. + * It allows to reset the state of the properties. If a selector is provided, the widget + * will only track the properties of the selected block, else it will track the properties + * of the current selectors provided by SelectedBlockSelectors property of the proxy. + */ +class PQCOMPONENTS_EXPORT pqMultiBlockPropertiesStateWidget : public QWidget +{ + Q_OBJECT +public: + typedef QWidget Superclass; + + pqMultiBlockPropertiesStateWidget(vtkSMProxy* repr, const QList& properties, + int iconSize, QString selector = QString(), QWidget* parent = nullptr, + Qt::WindowFlags f = Qt::WindowFlags()); + ~pqMultiBlockPropertiesStateWidget() override; + + using BlockPropertyState = vtkSMColorMapEditorHelper::BlockPropertyState; + + /** + * Get the state of the properties. + */ + BlockPropertyState getState() const; + + /* + * Get the Pixmap of the current state. + */ + QPixmap getStatePixmap(BlockPropertyState state) const; + + /** + * Get the tooltip of the current state. + */ + QString getStateToolTip(BlockPropertyState state) const; + + /** + * Get the reset button. + */ + pqHighlightableToolButton* getResetButton() const; + + /** + * Get the properties being tracked. + */ + std::vector getProperties() const; + + /** + * Get the selectors being tracked. + */ + std::vector getSelectors() const; + +Q_SIGNALS: + void stateChanged(BlockPropertyState newState); + void selectedBlockSelectorsChanged(); + void startStateReset(); + void endStateReset(); + +private Q_SLOTS: + void onSelectedBlockSelectorsChanged(); + void onPropertiesChanged(); + void onResetButtonClicked(); + +private: // NOLINT(readability-redundant-access-specifiers) + Q_DISABLE_COPY(pqMultiBlockPropertiesStateWidget) + + void setState(BlockPropertyState state); + + class pqInternals; + QScopedPointer Internals; +}; + +#endif // pqMultiBlockPropertiesStateWidget_h -- GitLab From 7d86452ce23ac88d85c8109e55a439514d5fbf82 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 14:23:04 -0400 Subject: [PATCH 21/43] pqMultiBlockPropertiesEditorWidget: Implementation This widget can be used to expose simple multiblock properties --- Plugins/Prism/Views/PrismViews.xml | 12 + .../pqStandardPropertyWidgetInterface.cxx | 7 +- Qt/Components/CMakeLists.txt | 1 + .../pqMultiBlockPropertiesEditorWidget.cxx | 284 ++++++++++++++++++ .../pqMultiBlockPropertiesEditorWidget.h | 39 +++ .../Resources/views_and_representations.xml | 99 ++++++ 6 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 Qt/Components/pqMultiBlockPropertiesEditorWidget.cxx create mode 100644 Qt/Components/pqMultiBlockPropertiesEditorWidget.h diff --git a/Plugins/Prism/Views/PrismViews.xml b/Plugins/Prism/Views/PrismViews.xml index 2e3232e9163..348bdc358ac 100644 --- a/Plugins/Prism/Views/PrismViews.xml +++ b/Plugins/Prism/Views/PrismViews.xml @@ -1126,6 +1126,18 @@ + + + + + + + + + + diff --git a/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx b/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx index 4b7f805ad7f..026376799e6 100644 --- a/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx +++ b/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx @@ -45,6 +45,7 @@ #include "pqListPropertyWidget.h" #include "pqMetaDataPropertyWidget.h" #include "pqMoleculePropertyWidget.h" +#include "pqMultiBlockPropertiesEditorWidget.h" #include "pqMultiComponentsDecorator.h" #include "pqOMETransferFunctionsPropertyWidget.h" #include "pqOSPRayHidingDecorator.h" @@ -223,7 +224,7 @@ pqPropertyWidget* pqStandardPropertyWidgetInterface::createWidgetForProperty( pqPropertyWidget* pqStandardPropertyWidgetInterface::createWidgetForPropertyGroup( vtkSMProxy* proxy, vtkSMPropertyGroup* group, QWidget* parentWidget) { - QString panelWidget(group->GetPanelWidget()); + const QString panelWidget(group->GetPanelWidget()); // *** NOTE: When adding new types, please update the header documentation *** if (panelWidget == "ColorEditor") { @@ -350,6 +351,10 @@ pqPropertyWidget* pqStandardPropertyWidgetInterface::createWidgetForPropertyGrou { return new pqSelectionListPropertyWidget(proxy, group, parentWidget); } + else if (panelWidget == "BlockPropertiesEditor") + { + return new pqMultiBlockPropertiesEditorWidget(proxy, group, parentWidget); + } // *** NOTE: When adding new types, please update the header documentation *** diff --git a/Qt/Components/CMakeLists.txt b/Qt/Components/CMakeLists.txt index d47737b2a8b..4fd0b8a4c60 100644 --- a/Qt/Components/CMakeLists.txt +++ b/Qt/Components/CMakeLists.txt @@ -88,6 +88,7 @@ set(classes pqLogViewerDialog pqMemoryInspectorPanel pqMultiBlockInspectorWidget + pqMultiBlockPropertiesEditorWidget pqMultiBlockPropertiesStateWidget pqMultiViewWidget pqOneLinerTextEdit diff --git a/Qt/Components/pqMultiBlockPropertiesEditorWidget.cxx b/Qt/Components/pqMultiBlockPropertiesEditorWidget.cxx new file mode 100644 index 00000000000..27864c53719 --- /dev/null +++ b/Qt/Components/pqMultiBlockPropertiesEditorWidget.cxx @@ -0,0 +1,284 @@ +// SPDX-FileCopyrightText: Copyright (c) Kitware Inc. +// SPDX-License-Identifier: BSD-3-Clause +#include "pqMultiBlockPropertiesEditorWidget.h" + +#include "pqCoreUtilities.h" +#include "pqMultiBlockPropertiesStateWidget.h" +#include "pqPropertiesPanel.h" +#include "pqPropertyWidget.h" +#include "pqProxyWidget.h" +#include "pqUndoStack.h" +#include "vtkSMColorMapEditorHelper.h" +#include "vtkSMProperty.h" +#include "vtkSMPropertyGroup.h" +#include "vtkSMProxy.h" +#include "vtkSMUncheckedPropertyHelper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//----------------------------------------------------------------------------- +class pqMultiBlockPropertiesEditorWidget::pqInternals : public QObject +{ +public: + pqInternals() { this->ColorMapEditorHelper->SetSelectedPropertiesTypeToBlocks(); } + + QPointer VerticalLayout; + + QPointer HelperProxyWidget; + QPointer HelperLayout; + + QPointer MapScalarsWidget; + QPointer MapScalarsStateWidget; + + QPointer InterpolateScalarsBeforeMappingWidget; + QPointer InterpolateScalarsBeforeMappingStateWidget; + + QPointer OpacityLabel; + QPointer OpacityWidget; + QPointer OpacityStateWidget; + + vtkNew ColorMapEditorHelper; +}; + +//----------------------------------------------------------------------------- +pqMultiBlockPropertiesEditorWidget::pqMultiBlockPropertiesEditorWidget( + vtkSMProxy* proxy, vtkSMPropertyGroup* smGroup, QWidget* parent) + : Superclass(proxy, smGroup, parent) + , Internals(new pqMultiBlockPropertiesEditorWidget::pqInternals()) +{ + auto& internals = *this->Internals; + + // create the layout + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth()); + this->setSizePolicy(sizePolicy); + internals.VerticalLayout = new QVBoxLayout(this); + internals.VerticalLayout->setSpacing(pqPropertiesPanel::suggestedVerticalSpacing()); + internals.VerticalLayout->setObjectName(QString::fromUtf8("verticalLayout")); + internals.VerticalLayout->setContentsMargins(pqPropertiesPanel::suggestedMargins()); + internals.VerticalLayout->setStretch(0, 1); + + // create a proxy widget with the listed properties + internals.HelperProxyWidget = new pqProxyWidget(proxy, + { "BlockMapScalarsGUI", "BlockInterpolateScalarsBeforeMappingsGUI", "BlockOpacitiesGUI" }, + { "multiblock_inspector" }, {}, true, this); + internals.HelperProxyWidget->setApplyChangesImmediately(false); + internals.HelperProxyWidget->updatePanel(); + + // add the proxy widget to the layout + // internals.Ui.verticalLayout->addWidget(internals.HelperProxyWidget); + internals.VerticalLayout->addWidget(internals.HelperProxyWidget); + + // extract the widgets from the proxy widget + internals.MapScalarsWidget = + internals.HelperProxyWidget->findChild("BlockMapScalarsGUI"); + internals.InterpolateScalarsBeforeMappingWidget = + internals.HelperProxyWidget->findChild( + "BlockInterpolateScalarsBeforeMappingsGUI"); + internals.OpacityWidget = + internals.HelperProxyWidget->findChild("BlockOpacitiesGUI"); + if (internals.OpacityWidget) + { + internals.OpacityLabel = internals.HelperProxyWidget->findChild( + QString("%1Label").arg(internals.OpacityWidget->objectName())); + } + internals.HelperLayout = internals.HelperProxyWidget->findChild(); + + // connect the widgets to the actual properties and create state widgets which you listen from + const int iconSize = std::max(this->style()->pixelMetric(QStyle::PM_SmallIconSize), 20); + const int numColumns = internals.HelperLayout->columnCount(); + if (internals.MapScalarsWidget) + { + internals.MapScalarsWidget->setEnabled(false); + QObject::connect(internals.MapScalarsWidget, &pqPropertyWidget::changeFinished, this, [&]() { + const QString undoText = tr("Change ") + + QCoreApplication::translate( + "ServerManagerXML", this->proxy()->GetProperty("BlockMapScalars")->GetXMLLabel()); + BEGIN_UNDO_SET(undoText); + const vtkSMUncheckedPropertyHelper mapScalarsHelper(internals.MapScalarsWidget->property()); + internals.ColorMapEditorHelper->SetSelectedMapScalars( + this->proxy(), mapScalarsHelper.GetAsString()); + Q_EMIT this->changeFinished(); + END_UNDO_SET(); + }); + + internals.MapScalarsStateWidget = new pqMultiBlockPropertiesStateWidget( + proxy, { "BlockMapScalars" }, iconSize, QString(), this); + internals.HelperLayout->addWidget( + internals.MapScalarsStateWidget, 1, numColumns, 1, 1, Qt::AlignVCenter); + QObject::connect(internals.MapScalarsStateWidget, + &pqMultiBlockPropertiesStateWidget::stateChanged, this, + &pqMultiBlockPropertiesEditorWidget::updateMapScalarsWidget); + QObject::connect(internals.MapScalarsStateWidget, + &pqMultiBlockPropertiesStateWidget::selectedBlockSelectorsChanged, this, + &pqMultiBlockPropertiesEditorWidget::updateMapScalarsWidget); + QObject::connect(internals.MapScalarsStateWidget, + &pqMultiBlockPropertiesStateWidget::endStateReset, this, + [&]() { Q_EMIT this->changeFinished(); }); + pqCoreUtilities::connect(this->proxy()->GetProperty("MapScalars"), vtkCommand::ModifiedEvent, + this, SLOT(updateMapScalarsWidget())); + } + if (internals.InterpolateScalarsBeforeMappingWidget) + { + internals.InterpolateScalarsBeforeMappingWidget->setEnabled(false); + QObject::connect(internals.InterpolateScalarsBeforeMappingWidget, + &pqPropertyWidget::changeFinished, this, [&]() { + const QString undoText = tr("Change ") + + QCoreApplication::translate("ServerManagerXML", + this->proxy()->GetProperty("BlockInterpolateScalarsBeforeMappings")->GetXMLLabel()); + BEGIN_UNDO_SET(undoText); + const vtkSMUncheckedPropertyHelper interpolateScalarsHelper( + internals.InterpolateScalarsBeforeMappingWidget->property()); + internals.ColorMapEditorHelper->SetSelectedInterpolateScalarsBeforeMapping( + this->proxy(), interpolateScalarsHelper.GetAsInt()); + Q_EMIT this->changeFinished(); + END_UNDO_SET(); + }); + + internals.InterpolateScalarsBeforeMappingStateWidget = new pqMultiBlockPropertiesStateWidget( + proxy, { "BlockInterpolateScalarsBeforeMappings" }, iconSize, QString(), this); + internals.HelperLayout->addWidget( + internals.InterpolateScalarsBeforeMappingStateWidget, 2, numColumns, 1, 1, Qt::AlignVCenter); + QObject::connect(internals.InterpolateScalarsBeforeMappingStateWidget, + &pqMultiBlockPropertiesStateWidget::stateChanged, this, + &pqMultiBlockPropertiesEditorWidget::updateInterpolateScalarsBeforeMappingWidget); + QObject::connect(internals.InterpolateScalarsBeforeMappingStateWidget, + &pqMultiBlockPropertiesStateWidget::selectedBlockSelectorsChanged, this, + &pqMultiBlockPropertiesEditorWidget::updateInterpolateScalarsBeforeMappingWidget); + QObject::connect(internals.InterpolateScalarsBeforeMappingStateWidget, + &pqMultiBlockPropertiesStateWidget::endStateReset, this, + [&]() { Q_EMIT this->changeFinished(); }); + pqCoreUtilities::connect(this->proxy()->GetProperty("InterpolateScalarsBeforeMapping"), + vtkCommand::ModifiedEvent, this, SLOT(updateInterpolateScalarsBeforeMappingWidget())); + } + if (internals.OpacityWidget) + { + internals.OpacityLabel->setEnabled(false); + internals.OpacityWidget->setEnabled(false); + QObject::connect(internals.OpacityWidget, &pqPropertyWidget::changeFinished, this, [&]() { + const QString undoText = tr("Change ") + + QCoreApplication::translate( + "ServerManagerXML", this->proxy()->GetProperty("BlockOpacities")->GetXMLLabel()); + BEGIN_UNDO_SET(undoText); + const vtkSMUncheckedPropertyHelper opacityHelper(internals.OpacityWidget->property()); + internals.ColorMapEditorHelper->SetSelectedOpacity( + this->proxy(), opacityHelper.GetAsDouble()); + Q_EMIT this->changeFinished(); + END_UNDO_SET(); + }); + + internals.OpacityStateWidget = + new pqMultiBlockPropertiesStateWidget(proxy, { "BlockOpacities" }, iconSize, QString(), this); + internals.HelperLayout->addWidget( + internals.OpacityStateWidget, 3, numColumns, 1, 1, Qt::AlignVCenter); + QObject::connect(internals.OpacityStateWidget, &pqMultiBlockPropertiesStateWidget::stateChanged, + this, &pqMultiBlockPropertiesEditorWidget::updateOpacityWidget); + QObject::connect(internals.OpacityStateWidget, + &pqMultiBlockPropertiesStateWidget::selectedBlockSelectorsChanged, this, + &pqMultiBlockPropertiesEditorWidget::updateOpacityWidget); + QObject::connect(internals.OpacityStateWidget, + &pqMultiBlockPropertiesStateWidget::endStateReset, this, + [&]() { Q_EMIT this->changeFinished(); }); + pqCoreUtilities::connect(this->proxy()->GetProperty("Opacity"), vtkCommand::ModifiedEvent, this, + SLOT(updateOpacityWidget())); + } +} + +//----------------------------------------------------------------------------- +pqMultiBlockPropertiesEditorWidget::~pqMultiBlockPropertiesEditorWidget() = default; + +//----------------------------------------------------------------------------- +void pqMultiBlockPropertiesEditorWidget::updateMapScalarsWidget() +{ + auto& internals = *this->Internals; + const bool prev = this->blockSignals(true); + auto state = internals.MapScalarsStateWidget->getState(); + // the state widget has already updated itself + internals.MapScalarsWidget->setEnabled(state != 0 /*Disabled*/); + + vtkSMUncheckedPropertyHelper mapScalarsHelper(internals.MapScalarsWidget->property()); + if (state <= pqMultiBlockPropertiesStateWidget::BlockPropertyState::RepresentationInherited) + { + mapScalarsHelper.Set(internals.ColorMapEditorHelper->GetMapScalars(this->proxy())); + } + else + { + auto selectedBlockSelectors = + internals.ColorMapEditorHelper->GetSelectedBlockSelectors(this->proxy()); + auto selectorAndState = internals.ColorMapEditorHelper->HasBlockProperty( + this->proxy(), selectedBlockSelectors, "BlockMapScalars"); + mapScalarsHelper.Set( + internals.ColorMapEditorHelper->GetBlockMapScalars(this->proxy(), selectorAndState.first)); + } + this->blockSignals(prev); +} + +//----------------------------------------------------------------------------- +void pqMultiBlockPropertiesEditorWidget::updateInterpolateScalarsBeforeMappingWidget() +{ + auto& internals = *this->Internals; + const bool prev = this->blockSignals(true); + + auto state = internals.InterpolateScalarsBeforeMappingStateWidget->getState(); + // the state widget has already updated itself + internals.InterpolateScalarsBeforeMappingWidget->setEnabled(state != 0 /*Disabled*/); + + vtkSMUncheckedPropertyHelper interpolateScalarsHelper( + internals.InterpolateScalarsBeforeMappingWidget->property()); + if (state <= pqMultiBlockPropertiesStateWidget::BlockPropertyState::RepresentationInherited) + { + interpolateScalarsHelper.Set( + internals.ColorMapEditorHelper->GetInterpolateScalarsBeforeMapping(this->proxy())); + } + else + { + auto selectedBlockSelectors = + internals.ColorMapEditorHelper->GetSelectedBlockSelectors(this->proxy()); + auto selectorAndState = internals.ColorMapEditorHelper->HasBlockProperty( + this->proxy(), selectedBlockSelectors, "BlockInterpolateScalarsBeforeMappings"); + interpolateScalarsHelper.Set( + internals.ColorMapEditorHelper->GetBlockInterpolateScalarsBeforeMapping( + this->proxy(), selectorAndState.first)); + } + this->blockSignals(prev); +} + +//----------------------------------------------------------------------------- +void pqMultiBlockPropertiesEditorWidget::updateOpacityWidget() +{ + auto& internals = *this->Internals; + const bool prev = this->blockSignals(true); + + auto state = internals.OpacityStateWidget->getState(); + // the state widget has already updated itself + internals.OpacityLabel->setEnabled(state != 0 /*Disabled*/); + internals.OpacityWidget->setEnabled(state != 0 /*Disabled*/); + + vtkSMUncheckedPropertyHelper opacityHelper(internals.OpacityWidget->property()); + if (state <= pqMultiBlockPropertiesStateWidget::BlockPropertyState::RepresentationInherited) + { + opacityHelper.Set(internals.ColorMapEditorHelper->GetOpacity(this->proxy())); + } + else + { + auto selectedBlockSelectors = + internals.ColorMapEditorHelper->GetSelectedBlockSelectors(this->proxy()); + auto selectorAndState = internals.ColorMapEditorHelper->HasBlockProperty( + this->proxy(), selectedBlockSelectors, "BlockOpacities"); + opacityHelper.Set( + internals.ColorMapEditorHelper->GetBlockOpacity(this->proxy(), selectorAndState.first)); + } + this->blockSignals(prev); +} diff --git a/Qt/Components/pqMultiBlockPropertiesEditorWidget.h b/Qt/Components/pqMultiBlockPropertiesEditorWidget.h new file mode 100644 index 00000000000..df904d2e69c --- /dev/null +++ b/Qt/Components/pqMultiBlockPropertiesEditorWidget.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright (c) Kitware Inc. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef pqMultiBlockPropertiesEditorWidget_h +#define pqMultiBlockPropertiesEditorWidget_h + +#include "pqComponentsModule.h" +#include "pqPropertyGroupWidget.h" + +#include + +/** + * This is a pqPropertyGroupWidget subclass that presents a widget to edit several + * representation properties of a multi-block dataset. It's used as the + * "widget" for \c BlockPropertiesEditor property group. + */ +class PQCOMPONENTS_EXPORT pqMultiBlockPropertiesEditorWidget : public pqPropertyGroupWidget +{ + Q_OBJECT +public: + typedef pqPropertyGroupWidget Superclass; + + pqMultiBlockPropertiesEditorWidget( + vtkSMProxy* proxy, vtkSMPropertyGroup* smGroup, QWidget* parent = nullptr); + ~pqMultiBlockPropertiesEditorWidget() override; + +private Q_SLOTS: + void updateMapScalarsWidget(); + void updateInterpolateScalarsBeforeMappingWidget(); + void updateOpacityWidget(); + +private: // NOLINT(readability-redundant-access-specifiers) + class pqInternals; + QScopedPointer Internals; + + Q_DISABLE_COPY(pqMultiBlockPropertiesEditorWidget) +}; + +#endif // pqMultiBlockPropertiesEditorWidget_h diff --git a/Remoting/Views/Resources/views_and_representations.xml b/Remoting/Views/Resources/views_and_representations.xml index 3cf856aa9ac..d449f8faccd 100644 --- a/Remoting/Views/Resources/views_and_representations.xml +++ b/Remoting/Views/Resources/views_and_representations.xml @@ -4713,6 +4713,17 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + + + + + + + @@ -5122,6 +5133,18 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + + + + + + + + @@ -8840,6 +8863,44 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + + For composite datasets, specify if to map scalars associated with selectors + on the assembly chosen using **Assembly**. + + + + + + + For composite datasets, specify if to interpolate scalars before mapping associated with selectors + on the assembly chosen using **Assembly**. + + + + + + + For composite datasets, specify opacities associated with selectors + on the assembly chosen using **Assembly**. + + + + + + + For composite datasets, specify if to map scalars associated with selectors + on the assembly chosen using **Assembly**. + + + + + + + For composite datasets, specify if to interpolate scalars before mapping associated with selectors + on the assembly chosen using **Assembly**. + + + + + + + For composite datasets, specify opacities associated with selectors + on the assembly chosen using **Assembly**. + + + Date: Mon, 13 May 2024 17:04:22 -0400 Subject: [PATCH 22/43] pqPropertyLinks: Add PropertyLinksConnections getters --- Qt/Core/pqPropertyLinks.cxx | 16 ++++++++++++++++ Qt/Core/pqPropertyLinks.h | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/Qt/Core/pqPropertyLinks.cxx b/Qt/Core/pqPropertyLinks.cxx index 84b8a9b2262..146406f40bd 100644 --- a/Qt/Core/pqPropertyLinks.cxx +++ b/Qt/Core/pqPropertyLinks.cxx @@ -129,6 +129,22 @@ bool pqPropertyLinks::removePropertyLink(QObject* qobject, const char* qproperty return false; } +//----------------------------------------------------------------------------- +int pqPropertyLinks::getNumberOfPropertyLinks() const +{ + return this->Internals->Connections.size(); +} + +//----------------------------------------------------------------------------- +pqPropertyLinksConnection* pqPropertyLinks::getPropertyLink(int index) const +{ + if (index < 0 || index >= this->Internals->Connections.size()) + { + return nullptr; + } + return this->Internals->Connections.value(index); +} + //----------------------------------------------------------------------------- void pqPropertyLinks::clear() { diff --git a/Qt/Core/pqPropertyLinks.h b/Qt/Core/pqPropertyLinks.h index 8af3eb26907..20047b75118 100644 --- a/Qt/Core/pqPropertyLinks.h +++ b/Qt/Core/pqPropertyLinks.h @@ -97,6 +97,16 @@ public: bool autoUpdateVTKObjects() const { return this->AutoUpdateVTKObjects; } bool useUncheckedProperties() const { return this->UseUncheckedProperties; } + /** + * Returns the number of property links. + */ + int getNumberOfPropertyLinks() const; + + /** + * Returns the property link at the given index. + */ + pqPropertyLinksConnection* getPropertyLink(int index) const; + public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) /** * Remove all links. -- GitLab From 5b3cdc180dde11ee314acf10b076627e4dce7876 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Mon, 13 May 2024 19:50:17 -0400 Subject: [PATCH 23/43] pqColorEditorPropertyWidget: Support block(s) coloring --- Plugins/Prism/Views/PrismViews.xml | 9 + .../pqColorEditorPropertyWidget.cxx | 197 ++++++++++++------ .../pqColorEditorPropertyWidget.h | 11 +- .../pqEditScalarBarReaction.cxx | 34 ++- .../pqEditScalarBarReaction.h | 9 + .../pqScalarBarVisibilityReaction.h | 3 +- .../pqStandardPropertyWidgetInterface.cxx | 7 +- .../pqUseSeparateColorMapReaction.cxx | 10 + .../pqUseSeparateColorMapReaction.h | 5 + Qt/Components/pqDisplayColorWidget.cxx | 12 ++ Qt/Components/pqDisplayColorWidget.h | 5 + .../Resources/views_and_representations.xml | 18 ++ 12 files changed, 248 insertions(+), 72 deletions(-) diff --git a/Plugins/Prism/Views/PrismViews.xml b/Plugins/Prism/Views/PrismViews.xml index 348bdc358ac..9c8dbed1cd7 100644 --- a/Plugins/Prism/Views/PrismViews.xml +++ b/Plugins/Prism/Views/PrismViews.xml @@ -1126,6 +1126,15 @@ + + + + + + + diff --git a/Qt/ApplicationComponents/pqColorEditorPropertyWidget.cxx b/Qt/ApplicationComponents/pqColorEditorPropertyWidget.cxx index 953033ee319..140b9d4d2a7 100644 --- a/Qt/ApplicationComponents/pqColorEditorPropertyWidget.cxx +++ b/Qt/ApplicationComponents/pqColorEditorPropertyWidget.cxx @@ -8,128 +8,201 @@ #include "pqDataRepresentation.h" #include "pqEditColorMapReaction.h" #include "pqEditScalarBarReaction.h" +#include "pqMultiBlockPropertiesStateWidget.h" #include "pqPropertiesPanel.h" #include "pqRescaleScalarRangeReaction.h" #include "pqScalarBarVisibilityReaction.h" #include "pqServerManagerModel.h" #include "pqUseSeparateColorMapReaction.h" -#include "vtkSMPropertyHelper.h" -class pqColorEditorPropertyWidget::pqInternals +#include "vtkSMColorMapEditorHelper.h" +#include "vtkSMProxy.h" + +#include +#include +#include +#include +#include + +#include +#include + +class pqColorEditorPropertyWidget::pqInternals : public QObject { public: Ui::ColorEditorPropertyWidget Ui; + + QPointer ScalarBarVisibilityReaction; + QPointer UseSeparateColorMapReaction; + QPointer ScalarBarVisibilityAction; - QPointer UseSeparateColorMapAction; QPointer EditScalarBarAction; + + QPointer Representation; + QPointer StateWidget; + + std::vector OldLuts; }; //----------------------------------------------------------------------------- -pqColorEditorPropertyWidget::pqColorEditorPropertyWidget(vtkSMProxy* smProxy, QWidget* parentObject) +pqColorEditorPropertyWidget::pqColorEditorPropertyWidget( + vtkSMProxy* smProxy, QWidget* parentObject, int selectedPropertiesType) : Superclass(smProxy, parentObject) , Internals(new pqColorEditorPropertyWidget::pqInternals()) { this->setShowLabel(true); + auto& internals = *this->Internals; - Ui::ColorEditorPropertyWidget& Ui = this->Internals->Ui; + Ui::ColorEditorPropertyWidget& Ui = internals.Ui; Ui.setupUi(this); - Ui.gridLayout->setContentsMargins(pqPropertiesPanel::suggestedMargin(), - pqPropertiesPanel::suggestedMargin(), pqPropertiesPanel::suggestedMargin(), - pqPropertiesPanel::suggestedMargin()); + Ui.gridLayout->setContentsMargins(pqPropertiesPanel::suggestedMargins()); Ui.gridLayout->setHorizontalSpacing(pqPropertiesPanel::suggestedHorizontalSpacing()); Ui.gridLayout->setVerticalSpacing(pqPropertiesPanel::suggestedVerticalSpacing()); - // Setup various widget properties. + // Get the representation. pqServerManagerModel* smm = pqApplicationCore::instance()->getServerManagerModel(); pqProxy* pqproxy = smm->findItem(smProxy); - pqDataRepresentation* representation = qobject_cast(pqproxy); - Ui.DisplayColorWidget->setRepresentation(representation); + internals.Representation = qobject_cast(pqproxy); + + // Setup various widget properties. + Ui.DisplayColorWidget->setRepresentation(internals.Representation, selectedPropertiesType); // show scalar bar button QAction* scalarBarAction = new QAction(this); this->Internals->ScalarBarVisibilityAction = scalarBarAction; - scalarBarAction->connect(Ui.ShowScalarBar, SIGNAL(clicked()), SLOT(trigger())); - Ui.ShowScalarBar->connect(scalarBarAction, SIGNAL(toggled(bool)), SLOT(setChecked(bool))); - pqScalarBarVisibilityReaction* sbvr = - new pqScalarBarVisibilityReaction(scalarBarAction, /*track_active_objects*/ false); - sbvr->setRepresentation(representation); - this->connect(scalarBarAction, SIGNAL(changed()), SLOT(updateEnableState())); + QObject::connect(Ui.ShowScalarBar, &QPushButton::clicked, scalarBarAction, &QAction::trigger); + QObject::connect(scalarBarAction, &QAction::toggled, Ui.ShowScalarBar, &QPushButton::setChecked); + this->Internals->ScalarBarVisibilityReaction = + new pqScalarBarVisibilityReaction(scalarBarAction, /*track_active_objects=*/false); + this->Internals->ScalarBarVisibilityReaction->setRepresentation( + internals.Representation, selectedPropertiesType); + QObject::connect( + scalarBarAction, &QAction::changed, this, &pqColorEditorPropertyWidget::updateEnableState); // edit scalar bar. QAction* editScalarBarAction = new QAction(this); this->Internals->EditScalarBarAction = editScalarBarAction; - editScalarBarAction->connect(Ui.EditScalarBar, SIGNAL(clicked()), SLOT(trigger())); - pqEditScalarBarReaction* esbr = new pqEditScalarBarReaction(editScalarBarAction, false); - esbr->setRepresentation(representation); - this->connect(editScalarBarAction, SIGNAL(changed()), SLOT(updateEnableState())); + QObject::connect(Ui.EditScalarBar, &QPushButton::clicked, editScalarBarAction, &QAction::trigger); + pqEditScalarBarReaction* esbr = + new pqEditScalarBarReaction(editScalarBarAction, /*track_active_objects=*/false); + esbr->setScalarBarVisibilityReaction(this->Internals->ScalarBarVisibilityReaction); + esbr->setRepresentation(internals.Representation, selectedPropertiesType); + QObject::connect( + editScalarBarAction, &QAction::changed, this, &pqColorEditorPropertyWidget::updateEnableState); // edit color map button QAction* editColorMapAction = new QAction(this); - QObject::connect(Ui.EditColorMap, SIGNAL(clicked()), editColorMapAction, SLOT(trigger())); - pqEditColorMapReaction* ecmr = new pqEditColorMapReaction(editColorMapAction, false); - ecmr->setRepresentation(representation); + QObject::connect(Ui.EditColorMap, &QPushButton::clicked, editColorMapAction, &QAction::trigger); + pqEditColorMapReaction* ecmr = + new pqEditColorMapReaction(editColorMapAction, /*track_active_objects=*/false); + ecmr->setRepresentation(internals.Representation, selectedPropertiesType); // separate color map button QAction* useSeparateColorMapAction = new QAction(this); - this->Internals->UseSeparateColorMapAction = useSeparateColorMapAction; - useSeparateColorMapAction->connect(Ui.UseSeparateColorMap, SIGNAL(clicked()), SLOT(trigger())); - Ui.UseSeparateColorMap->connect( - useSeparateColorMapAction, SIGNAL(toggled(bool)), SLOT(setChecked(bool))); - pqUseSeparateColorMapReaction* uscmr = - new pqUseSeparateColorMapReaction(useSeparateColorMapAction, Ui.DisplayColorWidget, false); - uscmr->setRepresentation(representation); - this->connect(useSeparateColorMapAction, SIGNAL(changed()), SLOT(updateEnableState())); + QObject::connect( + Ui.UseSeparateColorMap, &QPushButton::clicked, useSeparateColorMapAction, &QAction::trigger); + QObject::connect( + useSeparateColorMapAction, &QAction::toggled, Ui.UseSeparateColorMap, &QPushButton::setChecked); + this->Internals->UseSeparateColorMapReaction = new pqUseSeparateColorMapReaction( + useSeparateColorMapAction, Ui.DisplayColorWidget, /*track_active_objects=*/false); + this->Internals->UseSeparateColorMapReaction->setRepresentation( + internals.Representation, selectedPropertiesType); + QObject::connect(useSeparateColorMapAction, &QAction::changed, this, + &pqColorEditorPropertyWidget::updateEnableState); // reset range button QAction* resetRangeAction = new QAction(this); - QObject::connect(Ui.Rescale, SIGNAL(clicked()), resetRangeAction, SLOT(trigger())); - pqRescaleScalarRangeReaction* rsrr = new pqRescaleScalarRangeReaction(resetRangeAction, false); - rsrr->setRepresentation(representation); + QObject::connect(Ui.Rescale, &QPushButton::clicked, resetRangeAction, &QAction::trigger); + pqRescaleScalarRangeReaction* rsrr = new pqRescaleScalarRangeReaction( + resetRangeAction, /*track_active_objects=*/false, pqRescaleScalarRangeReaction::DATA); + rsrr->setRepresentation(internals.Representation, selectedPropertiesType); // reset custom range button QAction* resetCustomRangeAction = new QAction(this); - resetCustomRangeAction->connect(Ui.RescaleCustom, SIGNAL(clicked()), SLOT(trigger())); + QObject::connect( + Ui.RescaleCustom, &QPushButton::clicked, resetCustomRangeAction, &QAction::trigger); rsrr = new pqRescaleScalarRangeReaction( - resetCustomRangeAction, false, pqRescaleScalarRangeReaction::CUSTOM); - rsrr->setRepresentation(representation); + resetCustomRangeAction, /*track_active_objects=*/false, pqRescaleScalarRangeReaction::CUSTOM); + rsrr->setRepresentation(internals.Representation, selectedPropertiesType); // reset custom range button QAction* resetTemporalRangeAction = new QAction(this); - resetTemporalRangeAction->connect(Ui.RescaleTemporal, SIGNAL(clicked()), SLOT(trigger())); - rsrr = new pqRescaleScalarRangeReaction( - resetTemporalRangeAction, false, pqRescaleScalarRangeReaction::TEMPORAL); - rsrr->setRepresentation(representation); + QObject::connect( + Ui.RescaleTemporal, &QPushButton::clicked, resetTemporalRangeAction, &QAction::trigger); + rsrr = new pqRescaleScalarRangeReaction(resetTemporalRangeAction, /*track_active_objects=*/false, + pqRescaleScalarRangeReaction::TEMPORAL); + rsrr->setRepresentation(internals.Representation, selectedPropertiesType); // choose preset button. QAction* choosePresetAction = new QAction(this); - choosePresetAction->connect(Ui.ChoosePreset, SIGNAL(clicked()), SLOT(trigger())); - pqChooseColorPresetReaction* ccpr = new pqChooseColorPresetReaction(choosePresetAction, false); - ccpr->setRepresentation(representation); - representation->connect( - ccpr, SIGNAL(presetApplied(const QString&)), SLOT(renderViewEventually())); - + QObject::connect(Ui.ChoosePreset, &QPushButton::clicked, choosePresetAction, &QAction::trigger); + pqChooseColorPresetReaction* ccpr = + new pqChooseColorPresetReaction(choosePresetAction, /*track_active_objects=*/false); + ccpr->setRepresentation(internals.Representation, selectedPropertiesType); + QObject::connect(ccpr, &pqChooseColorPresetReaction::presetApplied, internals.Representation, + &pqDataRepresentation::renderViewEventually); + + if (selectedPropertiesType == vtkSMColorMapEditorHelper::SelectedPropertiesTypes::Blocks) + { + const int iconSize = std::max(this->style()->pixelMetric(QStyle::PM_SmallIconSize), 20); + this->Internals->StateWidget = new pqMultiBlockPropertiesStateWidget(smProxy, + { "BlockColors", "BlockColorArrayNames", "BlockUseSeparateColorMaps" }, iconSize, QString(), + this); + const int numColumns = Ui.gridLayout->columnCount(); + Ui.gridLayout->addWidget(this->Internals->StateWidget, 1, numColumns, 1, 1, Qt::AlignVCenter); + QObject::connect(this->Internals->StateWidget, &pqMultiBlockPropertiesStateWidget::stateChanged, + this, &pqColorEditorPropertyWidget::updateBlockBasedEnableState); + QObject::connect(this->Internals->StateWidget, + &pqMultiBlockPropertiesStateWidget::selectedBlockSelectorsChanged, this, + &pqColorEditorPropertyWidget::updateBlockBasedEnableState); + QObject::connect( + internals.StateWidget, &pqMultiBlockPropertiesStateWidget::startStateReset, this, [&]() { + this->Internals->OldLuts = + this->Internals->Representation->getLookupTableProxies(vtkSMColorMapEditorHelper::Blocks); + }); + QObject::connect( + internals.StateWidget, &pqMultiBlockPropertiesStateWidget::endStateReset, this, [&]() { + pqDisplayColorWidget::hideScalarBarsIfNotNeeded( + this->Internals->Representation->getViewProxy(), this->Internals->OldLuts); + this->Internals->Representation->renderViewEventually(); + }); + } this->updateEnableState(); } //----------------------------------------------------------------------------- -pqColorEditorPropertyWidget::~pqColorEditorPropertyWidget() +pqColorEditorPropertyWidget::~pqColorEditorPropertyWidget() = default; + +//----------------------------------------------------------------------------- +void pqColorEditorPropertyWidget::updateBlockBasedEnableState() { - delete this->Internals; - this->Internals = nullptr; + this->Internals->Ui.DisplayColorWidget->queryCurrentSelectedArray(); + this->Internals->UseSeparateColorMapReaction->querySelectedUseSeparateColorMap(); + this->Internals->ScalarBarVisibilityReaction->updateEnableState(); + this->updateEnableState(); } //----------------------------------------------------------------------------- void pqColorEditorPropertyWidget::updateEnableState() { - Ui::ColorEditorPropertyWidget& ui = this->Internals->Ui; - const QAction* sbva = this->Internals->ScalarBarVisibilityAction; - ui.ShowScalarBar->setEnabled(sbva->isEnabled()); - ui.UseSeparateColorMap->setEnabled(sbva->isEnabled()); - ui.Rescale->setEnabled(sbva->isEnabled()); - ui.RescaleCustom->setEnabled(sbva->isEnabled()); - ui.RescaleTemporal->setEnabled(sbva->isEnabled()); - ui.ChoosePreset->setEnabled(sbva->isEnabled()); - - const QAction* esba = this->Internals->EditScalarBarAction; - ui.EditScalarBar->setEnabled(esba->isEnabled()); + auto& internals = *this->Internals; + bool enabled = true; + if (!internals.StateWidget.isNull()) + { + // enable any widget only if there is at least a block selected + enabled = internals.StateWidget->getState() != + pqMultiBlockPropertiesStateWidget::BlockPropertyState::Disabled; + } + Ui::ColorEditorPropertyWidget& ui = internals.Ui; + ui.DisplayColorWidget->setEnabled(enabled); + ui.EditColorMap->setEnabled(enabled); + const bool isScalarBarVisibilityActionEnabled = internals.ScalarBarVisibilityAction->isEnabled(); + ui.UseSeparateColorMap->setEnabled(enabled && isScalarBarVisibilityActionEnabled); + ui.Rescale->setEnabled(enabled && isScalarBarVisibilityActionEnabled); + ui.RescaleCustom->setEnabled(enabled && isScalarBarVisibilityActionEnabled); + ui.RescaleTemporal->setEnabled(enabled && isScalarBarVisibilityActionEnabled); + ui.ChoosePreset->setEnabled(enabled && isScalarBarVisibilityActionEnabled); + ui.ShowScalarBar->setEnabled(enabled && isScalarBarVisibilityActionEnabled); + const bool isEditScalarBarActionEnabled = internals.EditScalarBarAction->isEnabled(); + ui.EditScalarBar->setEnabled(enabled && isEditScalarBarActionEnabled); } diff --git a/Qt/ApplicationComponents/pqColorEditorPropertyWidget.h b/Qt/ApplicationComponents/pqColorEditorPropertyWidget.h index da45def41df..16a3a448d37 100644 --- a/Qt/ApplicationComponents/pqColorEditorPropertyWidget.h +++ b/Qt/ApplicationComponents/pqColorEditorPropertyWidget.h @@ -6,10 +6,12 @@ #include "pqApplicationComponentsModule.h" #include "pqPropertyWidget.h" +#include + /** * This is a pqPropertyWidget subclass that presents a widget to edit the color * of a representation and other related functionality. It's used as the - * "widget" for \c ColorEditor property group. + * "widget" for \c ColorEditor/BlockColorEditor property group. */ class PQAPPLICATIONCOMPONENTS_EXPORT pqColorEditorPropertyWidget : public pqPropertyWidget { @@ -17,15 +19,18 @@ class PQAPPLICATIONCOMPONENTS_EXPORT pqColorEditorPropertyWidget : public pqProp public: typedef pqPropertyWidget Superclass; - pqColorEditorPropertyWidget(vtkSMProxy* proxy, QWidget* parent = nullptr); + pqColorEditorPropertyWidget(vtkSMProxy* proxy, QWidget* parent = nullptr, + int selectedPropertiesType = 0 /*Representation*/); ~pqColorEditorPropertyWidget() override; private Q_SLOTS: + void updateBlockBasedEnableState(); + void updateEnableState(); private: // NOLINT(readability-redundant-access-specifiers) class pqInternals; - pqInternals* Internals; + QScopedPointer Internals; Q_DISABLE_COPY(pqColorEditorPropertyWidget) }; diff --git a/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx b/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx index 418330a2442..a78c933f12b 100644 --- a/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx +++ b/Qt/ApplicationComponents/pqEditScalarBarReaction.cxx @@ -15,15 +15,14 @@ #include #include #include +#include //----------------------------------------------------------------------------- pqEditScalarBarReaction::pqEditScalarBarReaction(QAction* parentObject, bool track_active_objects) : Superclass(parentObject) { - QAction* tmp = new QAction(this); - this->SBVReaction = new pqScalarBarVisibilityReaction(tmp, track_active_objects); - QObject::connect(tmp, &QAction::changed, this, &pqEditScalarBarReaction::updateEnableState); - this->updateEnableState(); + this->setScalarBarVisibilityReaction( + this->createDefaultScalarBarVisibilityReaction(track_active_objects)); } //----------------------------------------------------------------------------- @@ -36,6 +35,33 @@ void pqEditScalarBarReaction::setRepresentation( this->SBVReaction->setRepresentation(repr, selectedPropertiesType); } +//----------------------------------------------------------------------------- +QPointer +pqEditScalarBarReaction::createDefaultScalarBarVisibilityReaction(bool track_active_objects) +{ + QAction* tmp = new QAction(this); + return new pqScalarBarVisibilityReaction(tmp, track_active_objects); +} + +//----------------------------------------------------------------------------- +void pqEditScalarBarReaction::setScalarBarVisibilityReaction( + pqScalarBarVisibilityReaction* reaction) +{ + if (this->SBVReaction) + { + this->SBVReaction->parentAction()->disconnect(this); + delete this->SBVReaction; + this->SBVReaction = nullptr; + } + if (reaction) + { + this->SBVReaction = reaction; + QObject::connect(reaction->parentAction(), &QAction::changed, this, + &pqEditScalarBarReaction::updateEnableState); + this->updateEnableState(); + } +} + //----------------------------------------------------------------------------- void pqEditScalarBarReaction::updateEnableState() { diff --git a/Qt/ApplicationComponents/pqEditScalarBarReaction.h b/Qt/ApplicationComponents/pqEditScalarBarReaction.h index a18f23f1b6f..2ffe01dafc5 100644 --- a/Qt/ApplicationComponents/pqEditScalarBarReaction.h +++ b/Qt/ApplicationComponents/pqEditScalarBarReaction.h @@ -45,6 +45,11 @@ public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) } ///@} + /** + * Set Scalar Bar Visibility Reaction + */ + void setScalarBarVisibilityReaction(pqScalarBarVisibilityReaction* reaction); + /** * Show the editor dialog for editing scalar bar properties. */ @@ -64,6 +69,10 @@ protected Q_SLOTS: private: Q_DISABLE_COPY(pqEditScalarBarReaction) + + QPointer createDefaultScalarBarVisibilityReaction( + bool track_active_objects); + QPointer SBVReaction; }; diff --git a/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h b/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h index 9153a4a5ed4..47d55dc6554 100644 --- a/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h +++ b/Qt/ApplicationComponents/pqScalarBarVisibilityReaction.h @@ -69,13 +69,12 @@ public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) */ void setScalarBarVisibility(bool visible); -protected Q_SLOTS: /** * Updates the enabled state. Applications need not explicitly call this. */ void updateEnableState() override; -protected: // NOLINT(readability-redundant-access-specifiers) +protected: /** * Called when the action is triggered. */ diff --git a/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx b/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx index 026376799e6..e3d3ffee5ba 100644 --- a/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx +++ b/Qt/ApplicationComponents/pqStandardPropertyWidgetInterface.cxx @@ -68,6 +68,7 @@ #include "pqViewTypePropertyWidget.h" #include "pqXYChartViewBoundsPropertyWidget.h" #include "pqYoungsMaterialPropertyWidget.h" + #include "vtkSMCompositeTreeDomain.h" #include "vtkSMProperty.h" #include "vtkSMPropertyGroup.h" @@ -228,7 +229,11 @@ pqPropertyWidget* pqStandardPropertyWidgetInterface::createWidgetForPropertyGrou // *** NOTE: When adding new types, please update the header documentation *** if (panelWidget == "ColorEditor") { - return new pqColorEditorPropertyWidget(proxy, parentWidget); + return new pqColorEditorPropertyWidget(proxy, parentWidget, 0 /*Representation*/); + } + else if (panelWidget == "BlockColorEditor") + { + return new pqColorEditorPropertyWidget(proxy, parentWidget, 1 /*Blocks*/); } else if (panelWidget == "CubeAxes") { diff --git a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx index f8fd23c40f6..3f5c4a7b822 100644 --- a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx +++ b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.cxx @@ -183,6 +183,16 @@ void pqUseSeparateColorMapReaction::updateEnableState() this->parentAction()->setChecked(isSep); } +//----------------------------------------------------------------------------- +void pqUseSeparateColorMapReaction::querySelectedUseSeparateColorMap() +{ + if (this->Representation && this->Links.getPropertyLink(0)) + { + // force the current selected use separate color map to be queried. + Q_EMIT this->Links.getPropertyLink(0)->smpropertyModified(); + } +} + //----------------------------------------------------------------------------- void pqUseSeparateColorMapReaction::onTriggered() { diff --git a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h index fc159f73af6..2ab48c2c202 100644 --- a/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h +++ b/Qt/ApplicationComponents/pqUseSeparateColorMapReaction.h @@ -51,6 +51,11 @@ public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) void setActiveRepresentation(); ///@} + /** + * Query the currently selected use separate color map from the property. + */ + void querySelectedUseSeparateColorMap(); + protected Q_SLOTS: /** * Updates the enabled state. Applications need not explicitly call this. diff --git a/Qt/Components/pqDisplayColorWidget.cxx b/Qt/Components/pqDisplayColorWidget.cxx index 3145ccf1375..f2e43e6571a 100644 --- a/Qt/Components/pqDisplayColorWidget.cxx +++ b/Qt/Components/pqDisplayColorWidget.cxx @@ -490,6 +490,18 @@ QIcon* pqDisplayColorWidget::itemIcon(int association, const QString& arrayName) } } +//----------------------------------------------------------------------------- +void pqDisplayColorWidget::queryCurrentSelectedArray() +{ + if (this->Representation && this->Internals->Links.getPropertyLink(0)) + { + // force the current selected array to be queried. + Q_EMIT this->Internals->Links.getPropertyLink(0)->smpropertyModified(); + // ensure that we are showing the correct selected component + this->updateColorTransferFunction(); + } +} + //----------------------------------------------------------------------------- void pqDisplayColorWidget::refreshColorArrayNames() { diff --git a/Qt/Components/pqDisplayColorWidget.h b/Qt/Components/pqDisplayColorWidget.h index 7de30ad5ba0..f806433d749 100644 --- a/Qt/Components/pqDisplayColorWidget.h +++ b/Qt/Components/pqDisplayColorWidget.h @@ -101,6 +101,11 @@ public Q_SLOTS: */ void setRepresentationText(const QString& text); + /** + * Query the currently selected array from the property. + */ + void queryCurrentSelectedArray(); + private Q_SLOTS: /** * fills up the Variables combo-box using the active representation's diff --git a/Remoting/Views/Resources/views_and_representations.xml b/Remoting/Views/Resources/views_and_representations.xml index d449f8faccd..c7e6441d23a 100644 --- a/Remoting/Views/Resources/views_and_representations.xml +++ b/Remoting/Views/Resources/views_and_representations.xml @@ -4713,6 +4713,15 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + + + + + @@ -5133,6 +5142,15 @@ OSPRay pathtracer may need to transfer default materials from client to server. + + + + + + + -- GitLab From 731ede406b2a8b47fb3ff2a3f4c03aec37a05f6b Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Tue, 14 May 2024 13:51:19 -0400 Subject: [PATCH 24/43] pqDataAssemblyPropertyWidget: Mark Selected Block Selectors These are used by the MultiBlock Inspector --- .../pqDataAssemblyPropertyWidget.cxx | 150 ++++++++++-------- .../pqDataAssemblyPropertyWidget.h | 23 +++ 2 files changed, 106 insertions(+), 67 deletions(-) diff --git a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx index e80723231ca..24b253aebc9 100644 --- a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx +++ b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx @@ -20,6 +20,7 @@ #include "vtkPVGeneralSettings.h" #include "pqSMAdaptor.h" +#include "pqUndoStack.h" #include "vtkCommand.h" #include "vtkDataAssembly.h" #include "vtkDataAssemblyUtilities.h" @@ -687,9 +688,11 @@ std::vector getSelectors(vtkSMProxy* selectionSource, vtkDataAssemb } //================================================================================= -void hookupActiveSelection( - vtkSMProxy* repr, QItemSelectionModel* selectionModel, pqDataAssemblyTreeModel* model) +void hookupActiveSelection(vtkSMProxy* repr, vtkSMProperty* selectedSelectors, + QItemSelectionModel* selectionModel, pqDataAssemblyTreeModel* model) { + const bool enableActiveSelectionProperty = + vtkPVGeneralSettings::GetInstance()->GetSelectOnClickMultiBlockInspector(); // This function handles hooking up the selectionModel to follow active selection // and vice-versa. selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", false); @@ -714,77 +717,92 @@ void hookupActiveSelection( std::transform(nodeIds.begin(), nodeIds.end(), std::inserter(selectors, selectors.end()), [&](int id) { return assembly->GetNodePath(id); }); - // make active selection. - auto producerPort = vtkSMPropertyHelper(repr, "Input").GetAsOutputPort(); - selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", true); - const std::string assemblyName = repr && repr->GetProperty("Assembly") - ? vtkSMPropertyHelper(repr, "Assembly").GetAsString() - : "Hierarchy"; - selectBlocks(producerPort, assemblyName, selectors); - selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", false); - }); - - auto selManager = pqPVApplicationCore::instance()->selectionManager(); - auto connection = - QObject::connect(selManager, &pqSelectionManager::selectionChanged, [=](pqOutputPort* port) { - // When user creates selection in the application, reflect it in the - // widget, if possible. - if (selectionModel->property("PQ_IGNORE_SELECTION_CHANGES").toBool()) + // mark selected selectors in the widget + if (auto selectedSelectorsProp = vtkSMStringVectorProperty::SafeDownCast(selectedSelectors)) { - return; + BEGIN_UNDO_EXCLUDE(); + selectedSelectorsProp->SetElements({ selectors.begin(), selectors.end() }); + repr->UpdateProperty(selectedSelectorsProp->GetXMLName()); + END_UNDO_EXCLUDE(); } - selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", true); - const auto assembly = model->dataAssembly(); - auto producerPort = vtkSMPropertyHelper(repr, "Input").GetAsOutputPort(); - if (port == nullptr || port->getOutputPortProxy() != producerPort || assembly == nullptr) + if (enableActiveSelectionProperty) { - // clear selection in the widget. - selectionModel->clearSelection(); + // make active selection. + auto producerPort = vtkSMPropertyHelper(repr, "Input").GetAsOutputPort(); + selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", true); + const std::string assemblyName = repr && repr->GetProperty("Assembly") + ? vtkSMPropertyHelper(repr, "Assembly").GetAsString() + : "Hierarchy"; + selectBlocks(producerPort, assemblyName, selectors); + selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", false); } - else - { - auto appendSelections = port->getSelectionInput(); - unsigned int numInputs = appendSelections - ? vtkSMPropertyHelper(appendSelections, "Input").GetNumberOfElements() - : 0; - for (unsigned int i = 0; i < numInputs; ++i) + }); + + if (enableActiveSelectionProperty) + { + auto selManager = pqPVApplicationCore::instance()->selectionManager(); + auto connection = + QObject::connect(selManager, &pqSelectionManager::selectionChanged, [=](pqOutputPort* port) { + // When user creates selection in the application, reflect it in the + // widget, if possible. + if (selectionModel->property("PQ_IGNORE_SELECTION_CHANGES").toBool()) { - // update selection. - auto selectionSource = vtkSMSourceProxy::SafeDownCast( - vtkSMPropertyHelper(appendSelections, "Input").GetAsProxy(i)); - auto selectors = getSelectors(selectionSource, assembly); - const auto nodes = assembly->SelectNodes(selectors); - QList iListNodes; - std::copy(nodes.begin(), nodes.end(), std::back_inserter(iListNodes)); - auto indexes = model->index(iListNodes); - QItemSelection selection; - for (const auto& idx : indexes) - { - selection.select(idx, idx); - } + return; + } + selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", true); - std::vector proxyModels; - auto currentModel = selectionModel->model(); - while (auto proxyModel = qobject_cast(currentModel)) - { - proxyModels.push_back(proxyModel); - currentModel = proxyModel->sourceModel(); - } - std::reverse(proxyModels.begin(), proxyModels.end()); - for (auto& proxyModel : proxyModels) + const auto assembly = model->dataAssembly(); + auto producerPort = vtkSMPropertyHelper(repr, "Input").GetAsOutputPort(); + if (port == nullptr || port->getOutputPortProxy() != producerPort || assembly == nullptr) + { + // clear selection in the widget. + selectionModel->clearSelection(); + } + else + { + auto appendSelections = port->getSelectionInput(); + const unsigned int numInputs = appendSelections + ? vtkSMPropertyHelper(appendSelections, "Input").GetNumberOfElements() + : 0; + for (unsigned int i = 0; i < numInputs; ++i) { - selection = proxyModel->mapSelectionFromSource(selection); + // update selection. + auto selectionSource = vtkSMSourceProxy::SafeDownCast( + vtkSMPropertyHelper(appendSelections, "Input").GetAsProxy(i)); + auto selectors = getSelectors(selectionSource, assembly); + const auto nodes = assembly->SelectNodes(selectors); + QList iListNodes; + std::copy(nodes.begin(), nodes.end(), std::back_inserter(iListNodes)); + auto indexes = model->index(iListNodes); + QItemSelection selection; + for (const auto& idx : indexes) + { + selection.select(idx, idx); + } + + std::vector proxyModels; + auto currentModel = selectionModel->model(); + while (auto proxyModel = qobject_cast(currentModel)) + { + proxyModels.push_back(proxyModel); + currentModel = proxyModel->sourceModel(); + } + std::reverse(proxyModels.begin(), proxyModels.end()); + for (auto& proxyModel : proxyModels) + { + selection = proxyModel->mapSelectionFromSource(selection); + } + selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); } - selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); } - } - selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", false); - }); + selectionModel->setProperty("PQ_IGNORE_SELECTION_CHANGES", false); + }); - // ensure that the connection is destroyed with the selectionModel dies. - QObject::connect(selectionModel, &QObject::destroyed, - [connection, selManager]() { selManager->disconnect(connection); }); + // ensure that the connection is destroyed with the selectionModel dies. + QObject::connect(selectionModel, &QObject::destroyed, + [connection, selManager]() { selManager->disconnect(connection); }); + } } //================================================================================= @@ -968,13 +986,11 @@ pqDataAssemblyPropertyWidget::pqDataAssemblyPropertyWidget( internals.Ui.removeOpacities, internals.Ui.removeAllOpacities); int linkActiveSelection = 0; - bool enableActiveSelectionProperty = - vtkPVGeneralSettings::GetInstance()->GetSelectOnClickMultiBlockInspector(); if (groupHints && groupHints->GetScalarAttribute("link_active_selection", &linkActiveSelection) && - linkActiveSelection == 1 && enableActiveSelectionProperty) + linkActiveSelection == 1) { - hookupActiveSelection( - smproxy, internals.Ui.hierarchy->selectionModel(), internals.AssemblyTreeModel); + hookupActiveSelection(smproxy, smgroup->GetProperty("SelectedSelectors"), + internals.Ui.hierarchy->selectionModel(), internals.AssemblyTreeModel); } // A domain that can provide us the assembly we use to show in this property. diff --git a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h index 197aa3076f4..3dfbe1e4133 100644 --- a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h +++ b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h @@ -68,6 +68,28 @@ * * * + * + * + * + * + * + * + * + * + * + * + * For composite datasets, specify the color array name associated with selectors + * on the assembly chosen using **Assembly**. + * + * + * + * + * + * * * + * * * * -- GitLab From 1e30bcd237386c937eac95596772efbb783dbd4e Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 17 May 2024 09:42:27 -0400 Subject: [PATCH 25/43] pqDataAssemblyPropertyWidget: Add State Widget Also remove opacities/colors since they are handled elsewhere now. --- .../UI/pqDataAssemblyPropertyWidget.ui | 218 ----- .../pqDataAssemblyPropertyWidget.cxx | 886 ++++++------------ .../pqDataAssemblyPropertyWidget.h | 106 +-- Qt/Components/pqDataAssemblyTreeModel.cxx | 6 +- 4 files changed, 308 insertions(+), 908 deletions(-) diff --git a/Qt/ApplicationComponents/Resources/UI/pqDataAssemblyPropertyWidget.ui b/Qt/ApplicationComponents/Resources/UI/pqDataAssemblyPropertyWidget.ui index c4a8c9a14ba..1a3529d7507 100644 --- a/Qt/ApplicationComponents/Resources/UI/pqDataAssemblyPropertyWidget.ui +++ b/Qt/ApplicationComponents/Resources/UI/pqDataAssemblyPropertyWidget.ui @@ -183,224 +183,6 @@ - - - Color - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - 10 - - - 16 - - - 16 - - - true - - - false - - - - - - - 1 - - - - - Add new entry - - - ... - - - - :/QtWidgets/Icons/pqPlus.svg:/QtWidgets/Icons/pqPlus.svg - - - - - - - false - - - Remove current entry - - - ... - - - - :/QtWidgets/Icons/pqMinus.svg:/QtWidgets/Icons/pqMinus.svg - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - Remove all entries - - - ... - - - - :/QtWidgets/Icons/pqDelete.svg:/QtWidgets/Icons/pqDelete.svg - - - - - - - - - - Opacity - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - 10 - - - 16 - - - 16 - - - true - - - false - - - - - - - 1 - - - - - Add new entry - - - ... - - - - :/QtWidgets/Icons/pqPlus.svg:/QtWidgets/Icons/pqPlus.svg - - - - - - - false - - - Remove current entry - - - ... - - - - :/QtWidgets/Icons/pqMinus.svg:/QtWidgets/Icons/pqMinus.svg - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - Remove all entries - - - ... - - - - :/QtWidgets/Icons/pqDelete.svg:/QtWidgets/Icons/pqDelete.svg - - - - - - - diff --git a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx index 24b253aebc9..bb5f0bad6e4 100644 --- a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx +++ b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.cxx @@ -7,183 +7,82 @@ #include "pqComboBoxDomain.h" #include "pqCoreUtilities.h" #include "pqDataAssemblyTreeModel.h" -#include "pqDoubleRangeDialog.h" +#include "pqDataRepresentation.h" +#include "pqDisplayColorWidget.h" #include "pqHeaderView.h" +#include "pqHighlightableToolButton.h" +#include "pqMultiBlockPropertiesStateWidget.h" #include "pqOutputPort.h" #include "pqPVApplicationCore.h" +#include "pqSMAdaptor.h" #include "pqSelectionManager.h" #include "pqServerManagerModel.h" #include "pqSignalAdaptors.h" #include "pqTreeViewExpandState.h" #include "pqTreeViewSelectionHelper.h" - -#include "vtkPVGeneralSettings.h" - -#include "pqSMAdaptor.h" #include "pqUndoStack.h" + #include "vtkCommand.h" #include "vtkDataAssembly.h" #include "vtkDataAssemblyUtilities.h" +#include "vtkDataAssemblyVisitor.h" #include "vtkNew.h" +#include "vtkObjectFactory.h" +#include "vtkPVGeneralSettings.h" #include "vtkPVXMLElement.h" +#include "vtkSMColorMapEditorHelper.h" #include "vtkSMCompositeTreeDomain.h" #include "vtkSMDataAssemblyDomain.h" #include "vtkSMOutputPort.h" +#include "vtkSMPVRepresentationProxy.h" #include "vtkSMProperty.h" #include "vtkSMPropertyGroup.h" #include "vtkSMPropertyHelper.h" +#include "vtkSMRepresentedArrayListDomain.h" #include "vtkSMSelectionHelper.h" #include "vtkSMSourceProxy.h" #include "vtkSMStringVectorProperty.h" #include "vtkSelectionSource.h" #include "vtkSmartPointer.h" -#include "vtkStringArray.h" -#include +#include +#include #include #include +#include #include #include -#include -#include #include -#include #include namespace detail { -constexpr int ColorRole = Qt::UserRole; -constexpr int OpacityRole = Qt::UserRole + 1; - -//----------------------------------------------------------------------------- -/** - * Creates a pixmap for color. - */ -//----------------------------------------------------------------------------- -QPixmap ColorPixmap(int iconSize, const QColor& color, bool inherited) -{ - QPixmap pixmap(iconSize, iconSize); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - painter.setRenderHint(QPainter::Antialiasing, true); - if (color.isValid()) - { - QPen pen = painter.pen(); - pen.setColor(Qt::black); - pen.setStyle(Qt::SolidLine); - pen.setWidth(1); - painter.setPen(pen); - - QBrush brush = painter.brush(); - brush.setColor(color); - brush.setStyle(inherited ? Qt::Dense5Pattern : Qt::SolidPattern); - painter.setBrush(brush); - } - else - { - QPen pen = painter.pen(); - pen.setColor(Qt::black); - pen.setStyle(Qt::DashLine); - pen.setWidth(1); - painter.setPen(pen); - - painter.setBrush(Qt::NoBrush); - } - const int radius = 3 * iconSize / 8; - painter.drawEllipse(QPoint(iconSize / 2, iconSize / 2), radius, radius); - return pixmap; -} - -//----------------------------------------------------------------------------- -/** - * Creates a pixmap for opacity. - */ -//----------------------------------------------------------------------------- -QPixmap OpacityPixmap(int iconSize, double opacity, bool inherited) -{ - QPixmap pixmap(iconSize, iconSize); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - painter.setRenderHint(QPainter::Antialiasing, true); - - if (opacity >= 0.0 && opacity <= 1.0) - { - QPen pen = painter.pen(); - pen.setColor(Qt::black); - pen.setStyle(Qt::SolidLine); - pen.setWidth(1); - painter.setPen(pen); - - QBrush brush = painter.brush(); - brush.setColor(Qt::gray); - brush.setStyle(inherited ? Qt::Dense7Pattern : Qt::SolidPattern); - painter.setBrush(brush); - } - else - { - QPen pen = painter.pen(); - pen.setColor(Qt::black); - pen.setStyle(Qt::DashLine); - pen.setWidth(1); - painter.setPen(pen); - - painter.setBrush(Qt::NoBrush); - opacity = 1.0; - } - - const int delta = 3 * iconSize / 4; - QRect rect(0, 0, delta, delta); - rect.moveCenter(QPoint(iconSize / 2, iconSize / 2)); - painter.drawPie(rect, 0, 5760 * opacity); - return pixmap; -} - +constexpr int BlockPropertiesStateRole = Qt::UserRole; +constexpr int BlockPropertiesResetRole = Qt::UserRole + 1; } // end of detail namespace { //================================================================================= /** - * A table model to show lists for chosen selectors + properties. This helps use - * show a compact/non-hierarchical view of selectors and properties like opacity - * or color specified. + * A table model to show lists for chosen selectors. This helps use + * show a compact/non-hierarchical view of selectors. */ -class TableModel : public QAbstractTableModel +class TableSelectorsModel : public QAbstractTableModel { using Superclass = QAbstractTableModel; public: - enum ModeT - { - SELECTORS, - COLORS, - OPACITIES, - }; - - TableModel(int pixmapSize, ModeT mode, QObject* prnt) + TableSelectorsModel(QObject* prnt) : Superclass(prnt) - , Mode(mode) - , PixmapSize(pixmapSize) { } - ~TableModel() override = default; + ~TableSelectorsModel() override = default; int columnCount(const QModelIndex&) const override { - switch (this->Mode) - { - case COLORS: - // selector, r, g, b - return 4; - case OPACITIES: - // selector, opacity - return 2; - case SELECTORS: - default: - // selector - return 1; - } + return 1; // only one column. } int rowCount(const QModelIndex&) const override { return this->Values.size(); } @@ -205,7 +104,6 @@ public: void setData(const QStringList& selectors) { - Q_ASSERT(this->Mode == SELECTORS); QList> values; for (const auto& value : selectors) { @@ -237,30 +135,9 @@ public: { switch (section) { + default: case 0: return "Selector"; - case 1: - return this->Mode == OPACITIES ? "Opacity" : "Red "; - case 2: - return "Green"; - case 3: - return "Blue "; - } - } - else if (role == Qt::DecorationRole) - { - switch (section) - { - case 1: - return this->Mode == OPACITIES - ? detail::OpacityPixmap(this->PixmapSize, 0.75, false) - : detail::ColorPixmap(this->PixmapSize, QColor(Qt::red), false); - - case 2: - return detail::ColorPixmap(this->PixmapSize, QColor(Qt::green), false); - - case 3: - return detail::ColorPixmap(this->PixmapSize, QColor(Qt::blue), false); } } return QVariant(); @@ -281,25 +158,6 @@ public: changed = true; } } - else if (this->Mode == OPACITIES) - { - if (pair.second != value) - { - pair.second = value; - changed = true; - } - } - else if (this->Mode == COLORS) - { - auto color = pair.second.value(); - double rgbF[3] = { color.redF(), color.greenF(), color.blueF() }; - rgbF[index.column() - 1] = value.toDouble(); - if (color != QColor::fromRgbF(rgbF[0], rgbF[1], rgbF[2])) - { - pair.second = QColor::fromRgbF(rgbF[0], rgbF[1], rgbF[2]); - changed = true; - } - } if (changed) { @@ -320,34 +178,6 @@ public: case Qt::ToolTipRole: case Qt::EditRole: return pair.first; - case Qt::DecorationRole: - return this->Mode == COLORS - ? detail::ColorPixmap(this->PixmapSize, pair.second.value(), false) - : detail::OpacityPixmap(this->PixmapSize, pair.second.value(), false); - } - } - else if (indx.column() == 1 && this->Mode == OPACITIES) - { - switch (role) - { - case Qt::DisplayRole: - case Qt::EditRole: - case Qt::ToolTipRole: - return QString::number(pair.second.toDouble(), 'f', 2); - } - } - else if (indx.column() >= 1 && indx.column() < 4 && this->Mode == COLORS) - { - switch (role) - { - case Qt::DisplayRole: - case Qt::ToolTipRole: - case Qt::EditRole: - { - const auto color = pair.second.value(); - const double rgbF[3] = { color.redF(), color.greenF(), color.blueF() }; - return QString::number(rgbF[indx.column() - 1], 'f', 2); - } } } return QVariant(); @@ -385,24 +215,49 @@ public: } private: - Q_DISABLE_COPY(TableModel); - ModeT Mode; - int PixmapSize; + Q_DISABLE_COPY(TableSelectorsModel); QList> Values; }; +class CallbackDataVisitor : public vtkDataAssemblyVisitor +{ +public: + static CallbackDataVisitor* New(); + vtkTypeMacro(CallbackDataVisitor, vtkDataAssemblyVisitor); + + std::function VisitCallback; + void Visit(int nodeid) override + { + if (this->VisitCallback) + { + this->VisitCallback(nodeid); + } + } + +protected: + CallbackDataVisitor() = default; + ~CallbackDataVisitor() override = default; + +private: + CallbackDataVisitor(const CallbackDataVisitor&) = delete; + void operator=(const CallbackDataVisitor&) = delete; +}; +vtkStandardNewMacro(CallbackDataVisitor); + //================================================================================= /** * Specialization to ensure the header checkstate/label etc. reflects the root-node. - * This also handles mapping custom roles such as detail::ColorRole and detail::OpacityRole - * to columns and rendering appropriate swatches for the same. + * This also handles mapping the custom detail::BlockPropertiesStateRole and + * detail::BlockPropertiesResetRole roles to columns, + * and rendering appropriate swatches for the same. */ class pqDAPModel : public QIdentityProxyModel { - int PixmapSize = 16; + const int PixmapSize; using Superclass = QIdentityProxyModel; QString HeaderText; // header text for column 0. + public: pqDAPModel(int pixmapSize, QObject* prnt) : Superclass(prnt) @@ -417,6 +272,8 @@ public: }); } + ~pqDAPModel() override { this->resetStateWidgets(); } + void setHeaderText(const QString& txt) { if (this->HeaderText != txt) @@ -426,6 +283,24 @@ public: } } + void setPropertyNames(const QList& propertyNames) + { + if (this->PropertyNames != propertyNames) + { + this->PropertyNames = propertyNames; + } + } + + void setRepresentation(vtkSMProxy* repr) + { + pqServerManagerModel* smm = pqApplicationCore::instance()->getServerManagerModel(); + auto pqRepr = qobject_cast(smm->findItem(repr)); + if (this->Representation != pqRepr) + { + this->Representation = pqRepr; + } + } + void setSourceModel(QAbstractItemModel* smodel) override { this->Superclass::setSourceModel(smodel); @@ -448,6 +323,20 @@ public: } } + void initializeStateWidgets(vtkDataAssembly* assembly) + { + if (!assembly || this->ExtraColumns.empty()) + { + return; + } + this->resetStateWidgets(); + vtkNew visitor; + visitor->VisitCallback = [&](int nodeId) -> void { + this->StateWidgets[nodeId] = this->createStateWidget(nodeId); + }; + assembly->Visit(visitor); + } + QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { @@ -482,64 +371,55 @@ public: return this->Superclass::setHeaderData(section, orientation, value, role); } - int columnCount(const QModelIndex& indx = QModelIndex()) const override + int columnCount(const QModelIndex& index = QModelIndex()) const override { - return this->Superclass::columnCount(indx) + static_cast(this->ExtraColumns.size()); + return this->Superclass::columnCount(index) + static_cast(this->ExtraColumns.size()); } - QVariant data(const QModelIndex& indx, int role) const override + QVariant data(const QModelIndex& index, int role) const override { - if (indx.column() == 0 || !indx.isValid()) + if (index.column() == 0 || !index.isValid()) { - return this->Superclass::data(indx, role); + return this->Superclass::data(index, role); } - const int columnRole = this->ExtraColumns.at(indx.column() - 1); - auto isDerived = [&]() { - const auto var = this->Superclass::data( - indx.siblingAtColumn(0), pqDataAssemblyTreeModel::GetIsDerivedRole(columnRole)); - return var.isValid() == false || var.toBool(); - }; - - if (columnRole == detail::ColorRole) + const int columnRole = this->ExtraColumns.at(index.column() - 1); + if (columnRole == detail::BlockPropertiesStateRole) { + pqMultiBlockPropertiesStateWidget* stateWidget = this->getStateWidget(index); + if (!stateWidget) + { + return QVariant(); + } switch (role) { case Qt::DecorationRole: - { - const auto itemdata = this->Superclass::data(indx.siblingAtColumn(0), columnRole); - return detail::ColorPixmap(this->PixmapSize, itemdata.value(), isDerived()); - } - + return stateWidget->getStatePixmap(stateWidget->getState()); case Qt::ToolTipRole: - return isDerived() - ? QVariant("Double-click to set color.") - : QVariant("Double-click to change color,\nControl-click to remove color."); - break; - - case detail::ColorRole: - return this->Superclass::data(indx.siblingAtColumn(0), detail::ColorRole); + return stateWidget->getStateToolTip(stateWidget->getState()); + case detail::BlockPropertiesStateRole: + return this->Superclass::data(index.siblingAtColumn(0), detail::BlockPropertiesStateRole); + default: + return QVariant(); } } - else if (columnRole == detail::OpacityRole) + else if (columnRole == detail::BlockPropertiesResetRole) { + pqMultiBlockPropertiesStateWidget* stateWidget = this->getStateWidget(index); + if (!stateWidget) + { + return QVariant(); + } switch (role) { case Qt::DecorationRole: - { - const auto itemdata = this->Superclass::data(indx.siblingAtColumn(0), columnRole); - return detail::OpacityPixmap( - this->PixmapSize, itemdata.isValid() ? itemdata.toDouble() : -1.0, isDerived()); - } - + return stateWidget->getResetButton()->grab(); case Qt::ToolTipRole: - return isDerived() - ? QVariant("Double-click to set opacity.") - : QVariant("Double-click to change opacity,\nControl-click to remove opacity."); - break; - - case detail::OpacityRole: - return this->Superclass::data(indx.siblingAtColumn(0), detail::OpacityRole); + return stateWidget->getResetButton()->toolTip(); + case detail::BlockPropertiesResetRole: + return this->Superclass::data(index.siblingAtColumn(0), detail::BlockPropertiesResetRole); + default: + return QVariant(); } } return QVariant(); @@ -571,9 +451,80 @@ public: } } + pqMultiBlockPropertiesStateWidget* getStateWidget(const QModelIndex& index) const + { + const int nodeId = this->getNodeId(index); + const auto& result = this->StateWidgets.find(nodeId); + return result != this->StateWidgets.end() ? result.value() : nullptr; + } + private: Q_DISABLE_COPY(pqDAPModel); + + int getNodeId(const QModelIndex& index) const + { + auto treeModel = qobject_cast(this->sourceModel()); + return treeModel->nodeId(index); + } + + pqMultiBlockPropertiesStateWidget* createStateWidget(const int nodeId) + { + auto treeModel = qobject_cast(this->sourceModel()); + const QString selector = QString::fromStdString(treeModel->dataAssembly()->GetNodePath(nodeId)); + + pqMultiBlockPropertiesStateWidget* widget = new pqMultiBlockPropertiesStateWidget( + this->Representation->getProxy(), this->PropertyNames, this->PixmapSize, selector); + const bool hasBlockColorArrayNames = + std::find(this->PropertyNames.begin(), this->PropertyNames.end(), "BlockColorArrayNames") != + this->PropertyNames.end(); + if (hasBlockColorArrayNames) + { + QObject::connect(widget, &pqMultiBlockPropertiesStateWidget::startStateReset, this, + [this, nodeId]() -> void { + this->OldLuts[nodeId] = + this->Representation->getLookupTableProxies(vtkSMColorMapEditorHelper::Blocks); + }); + } + QObject::connect(widget, &pqMultiBlockPropertiesStateWidget::endStateReset, this, + [this, nodeId, hasBlockColorArrayNames]() -> void { + if (hasBlockColorArrayNames) + { + pqDisplayColorWidget::hideScalarBarsIfNotNeeded( + this->Representation->getViewProxy(), this->OldLuts[nodeId]); + } + Q_EMIT qobject_cast(this->parent())->changeFinished(); + }); + QObject::connect(widget, &pqMultiBlockPropertiesStateWidget::stateChanged, this, + [this, nodeId](pqMultiBlockPropertiesStateWidget::BlockPropertyState) { + auto sourceModel = qobject_cast(this->sourceModel()); + QModelIndex sourceModelIndex = sourceModel->index(0); + // TODO: The above line forces the entire model to be updated, which is not great, + // but since the below line doesn't work as expected, we have to do this. + // QModelIndex sourceModelIndex = sourceModel->index(nodeId); + QModelIndex modelIndex = this->mapFromSource(sourceModelIndex); + const QModelIndex stateIndex = modelIndex.siblingAtColumn(1); + const QModelIndex resetIndex = modelIndex.siblingAtColumn(2); + Q_EMIT this->dataChanged(stateIndex, resetIndex, { Qt::DecorationRole, Qt::ToolTipRole }); + }); + return widget; + } + + void resetStateWidgets() + { + for (auto& widget : this->StateWidgets) + { + delete widget; + } + this->StateWidgets.clear(); + } + std::vector ExtraColumns; + + QList PropertyNames; + QPointer Representation; + + QMap StateWidgets; + QMap> OldLuts; }; //================================================================================= @@ -588,7 +539,7 @@ void adjustHeader(QHeaderView* header) } //================================================================================= -void hookupTableView(QTableView* view, TableModel* model, QAbstractButton* addButton, +void hookupTableView(QTableView* view, TableSelectorsModel* model, QAbstractButton* addButton, QAbstractButton* removeButton, QAbstractButton* removeAllButton) { // hookup add button @@ -805,38 +756,6 @@ void hookupActiveSelection(vtkSMProxy* repr, vtkSMProperty* selectedSelectors, } } -//================================================================================= -template -QList colorsToVariantList(const QList>& colors) -{ - QList result; - for (const auto& pair : colors) - { - result.push_back(pair.first); - - const QVariant& variant = pair.second; - - auto color = variant.value(); - result.push_back(color.redF()); - result.push_back(color.greenF()); - result.push_back(color.blueF()); - } - return result; -} - -//================================================================================= -template -QList opacitiesToVariantList(const QList>& opacities) -{ - QList result; - for (const auto& pair : opacities) - { - result.push_back(pair.first); - result.push_back(pair.second.toDouble()); - } - return result; -} - //================================================================================= QList compositeIndicesToVariantList(const std::vector& cids) { @@ -870,25 +789,16 @@ public: QPointer AssemblyTreeModel; QPointer ProxyModel; - ///@{ /** - * Table models for advanced editing of the selectors + properties. + * A Table model for advanced editing of the selectors. * This is not supported in composite-indices mode. */ - QPointer SelectorsTableModel; - QPointer ColorsTableModel; - QPointer OpacitiesTableModel; - ///@} + QPointer SelectorsTableModel; ///@{ // Cached values. QStringList Selectors; - QList Colors; - QList Opacities; - QList CompositeIndices; - QList CompositeIndexColors; - QList CompositeIndexOpacities; ///@} bool BlockUpdates = false; @@ -948,29 +858,19 @@ pqDataAssemblyPropertyWidget::pqDataAssemblyPropertyWidget( internals.UseInputNameAsHeader = true; } - // Add a sort-filter model to enable sorting and filtering of item in the - // tree. - auto sortmodel = new QSortFilterProxyModel(this); - sortmodel->setSourceModel(internals.ProxyModel); - sortmodel->setRecursiveFilteringEnabled(true); - internals.Ui.hierarchy->setModel(sortmodel); + // Add a sort-filter model to enable sorting and filtering of item in the tree. + auto sortModel = new QSortFilterProxyModel(this); + sortModel->setSourceModel(internals.ProxyModel); + sortModel->setRecursiveFilteringEnabled(true); + internals.Ui.hierarchy->setModel(sortModel); - internals.SelectorsTableModel = new TableModel(iconSize, TableModel::SELECTORS, this); + internals.SelectorsTableModel = new ::TableSelectorsModel(this); internals.Ui.table->setModel(internals.SelectorsTableModel); - internals.ColorsTableModel = new TableModel(iconSize, TableModel::COLORS, this); - internals.Ui.colors->setModel(internals.ColorsTableModel); - internals.Ui.colors->horizontalHeader()->setDefaultSectionSize(iconSize + 6); - internals.Ui.colors->horizontalHeader()->setMinimumSectionSize(iconSize + 6); - - internals.OpacitiesTableModel = new TableModel(iconSize, TableModel::OPACITIES, this); - internals.Ui.opacities->setModel(internals.OpacitiesTableModel); - internals.Ui.opacities->horizontalHeader()->setDefaultSectionSize(iconSize + 6); - internals.Ui.opacities->horizontalHeader()->setMinimumSectionSize(iconSize + 6); - // change pqTreeView header. internals.Ui.hierarchy->setupCustomHeader(/*use_pqHeaderView=*/true); new pqTreeViewSelectionHelper(internals.Ui.hierarchy); + // TODO maintain selections even when another GUI element is selected if (auto headerView = qobject_cast(internals.Ui.hierarchy->header())) { headerView->setToggleCheckStateOnSectionClick(false); @@ -980,10 +880,6 @@ pqDataAssemblyPropertyWidget::pqDataAssemblyPropertyWidget( hookupTableView(internals.Ui.table, internals.SelectorsTableModel, internals.Ui.add, internals.Ui.remove, internals.Ui.removeAll); - hookupTableView(internals.Ui.colors, internals.ColorsTableModel, internals.Ui.addColors, - internals.Ui.removeColors, internals.Ui.removeAllColors); - hookupTableView(internals.Ui.opacities, internals.OpacitiesTableModel, internals.Ui.addOpacities, - internals.Ui.removeOpacities, internals.Ui.removeAllOpacities); int linkActiveSelection = 0; if (groupHints && groupHints->GetScalarAttribute("link_active_selection", &linkActiveSelection) && @@ -1088,111 +984,62 @@ pqDataAssemblyPropertyWidget::pqDataAssemblyPropertyWidget( internals.Ui.assemblyCombo->hide(); } - if (auto smproperty = smgroup->GetProperty("Colors")) + if (auto repr = vtkSMPVRepresentationProxy::SafeDownCast(smproxy)) { - internals.ProxyModel->addColumn(detail::ColorRole); - internals.AssemblyTreeModel->setRoleProperty( - detail::ColorRole, pqDataAssemblyTreeModel::InheritedUntilOverridden); - - if (vtkSMDomain* colorsDomain = smproperty->FindDomain()) + QList blockPropertyNames; + auto blockColors = vtkSMStringVectorProperty::SafeDownCast(repr->GetProperty("BlockColors")); + if (blockColors && blockColors->FindDomain()) { - // compositeIndices mode is exclusive: either all properties in the group are using it - // or none are. - Q_ASSERT(!usingCompositeIndices.isValid() || usingCompositeIndices.toBool() == true); - - usingCompositeIndices = true; - - domain = domain ? domain : colorsDomain; - - // remove colors tab, we only show it when not in composite indices mode. - internals.Ui.tabWidget->removeTab(internals.Ui.tabWidget->indexOf(internals.Ui.colorsTab)); - - this->addPropertyLink(this, "compositeIndexColors", SIGNAL(colorsChanged()), smproperty); + blockPropertyNames.push_back("BlockColors"); } - else if ((colorsDomain = smproperty->FindDomain())) + auto blockColorArrayNames = + vtkSMStringVectorProperty::SafeDownCast(repr->GetProperty("BlockColorArrayNames")); + if (blockColorArrayNames && blockColorArrayNames->FindDomain()) { - // compositeIndices mode is exclusive: either all properties in the group are using it - // or none are. - Q_ASSERT(!usingCompositeIndices.isValid() || usingCompositeIndices.toBool() == false); - - usingCompositeIndices = false; - - domain = domain ? domain : colorsDomain; - - // monitor ColorsTableModel. - QObject::connect(internals.ColorsTableModel.data(), &QAbstractItemModel::dataChanged, this, - &pqDataAssemblyPropertyWidget::colorsTableModified); - QObject::connect(internals.ColorsTableModel.data(), &QAbstractItemModel::rowsInserted, this, - &pqDataAssemblyPropertyWidget::colorsTableModified); - QObject::connect(internals.ColorsTableModel.data(), &QAbstractItemModel::rowsRemoved, this, - &pqDataAssemblyPropertyWidget::colorsTableModified); - QObject::connect(internals.ColorsTableModel.data(), &QAbstractItemModel::modelReset, this, - &pqDataAssemblyPropertyWidget::colorsTableModified); - - this->addPropertyLink(this, "selectorColors", SIGNAL(colorsChanged()), smproperty); + blockPropertyNames.push_back("BlockColorArrayNames"); } - else + auto blockUseSeparateColorMaps = + vtkSMStringVectorProperty::SafeDownCast(repr->GetProperty("BlockUseSeparateColorMaps")); + if (blockUseSeparateColorMaps && + blockUseSeparateColorMaps->FindDomain()) { - qWarning("Missing suitable domain on property for 'Colors'"); + blockPropertyNames.push_back("BlockUseSeparateColorMaps"); } - } - else - { - internals.Ui.tabWidget->removeTab(internals.Ui.tabWidget->indexOf(internals.Ui.colorsTab)); - } - - if (auto smproperty = smgroup->GetProperty("Opacities")) - { - internals.ProxyModel->addColumn(detail::OpacityRole); - internals.AssemblyTreeModel->setRoleProperty( - detail::OpacityRole, pqDataAssemblyTreeModel::InheritedUntilOverridden); - - if (vtkSMDomain* opacitiesDomain = smproperty->FindDomain()) + auto blockMapScalars = + vtkSMStringVectorProperty::SafeDownCast(repr->GetProperty("BlockMapScalars")); + if (blockMapScalars && blockMapScalars->FindDomain()) { - // compositeIndices mode is exclusive: either all properties in the group are using it - // or none are. - Q_ASSERT(!usingCompositeIndices.isValid() || usingCompositeIndices.toBool() == true); - - usingCompositeIndices = true; - - domain = domain ? domain : opacitiesDomain; - - // remove opacities tab; we only show it when not in composite indices - // mode. - internals.Ui.tabWidget->removeTab(internals.Ui.tabWidget->indexOf(internals.Ui.opacitiesTab)); - this->addPropertyLink( - this, "compositeIndexOpacities", SIGNAL(opacitiesChanged()), smproperty); + blockPropertyNames.push_back("BlockMapScalars"); + } + auto blockInterpolateScalarsBeforeMappings = vtkSMStringVectorProperty::SafeDownCast( + repr->GetProperty("BlockInterpolateScalarsBeforeMappings")); + if (blockInterpolateScalarsBeforeMappings && + blockInterpolateScalarsBeforeMappings->FindDomain()) + { + blockPropertyNames.push_back("BlockInterpolateScalarsBeforeMappings"); + } + auto blockOpacities = + vtkSMStringVectorProperty::SafeDownCast(repr->GetProperty("BlockOpacities")); + if (blockOpacities && blockOpacities->FindDomain()) + { + blockPropertyNames.push_back("BlockOpacities"); } - else if ((opacitiesDomain = smproperty->FindDomain())) + if (!blockPropertyNames.empty()) { - // compositeIndices mode is exclusive: either all properties in the group are using it - // or none are. Q_ASSERT(!usingCompositeIndices.isValid() || usingCompositeIndices.toBool() == false); usingCompositeIndices = false; - domain = domain ? domain : opacitiesDomain; - - // monitor OpacitiesTableModel. - QObject::connect(internals.OpacitiesTableModel.data(), &QAbstractItemModel::dataChanged, this, - &pqDataAssemblyPropertyWidget::opacitiesTableModified); - QObject::connect(internals.OpacitiesTableModel.data(), &QAbstractItemModel::rowsInserted, - this, &pqDataAssemblyPropertyWidget::opacitiesTableModified); - QObject::connect(internals.OpacitiesTableModel.data(), &QAbstractItemModel::rowsRemoved, this, - &pqDataAssemblyPropertyWidget::opacitiesTableModified); - QObject::connect(internals.OpacitiesTableModel.data(), &QAbstractItemModel::modelReset, this, - &pqDataAssemblyPropertyWidget::opacitiesTableModified); - this->addPropertyLink(this, "selectorOpacities", SIGNAL(opacitiesChanged()), smproperty); - } - else - { - qWarning("Missing suitable domain on property for 'Opacities'"); + internals.ProxyModel->setRepresentation(repr); + internals.ProxyModel->setPropertyNames(blockPropertyNames); + internals.ProxyModel->addColumn(detail::BlockPropertiesStateRole); + internals.ProxyModel->addColumn(detail::BlockPropertiesResetRole); + internals.AssemblyTreeModel->setRoleProperty( + detail::BlockPropertiesStateRole, pqDataAssemblyTreeModel::InheritedUntilOverridden); + internals.AssemblyTreeModel->setRoleProperty( + detail::BlockPropertiesResetRole, pqDataAssemblyTreeModel::InheritedUntilOverridden); } } - else - { - internals.Ui.tabWidget->removeTab(internals.Ui.tabWidget->indexOf(internals.Ui.opacitiesTab)); - } internals.InCompositeIndicesMode = (usingCompositeIndices.isValid() && usingCompositeIndices.toBool()); @@ -1219,64 +1066,48 @@ pqDataAssemblyPropertyWidget::pqDataAssemblyPropertyWidget( auto header = internals.Ui.hierarchy->header(); if (header->count() > 1) { - // hookup double click to edit color and opacity. - QObject::connect( - internals.Ui.hierarchy, &QTreeView::doubleClicked, [&](const QModelIndex& idx) { - auto model = internals.Ui.hierarchy->model(); - switch (internals.ProxyModel->roleForColumn(idx.column())) + // hookup pressed to reset state. + QObject::connect(internals.Ui.hierarchy, &QTreeView::pressed, [&](const QModelIndex& idx) { + auto model = internals.Ui.hierarchy->model(); + auto sourceIndex = qobject_cast(model)->mapToSource(idx); + switch (internals.ProxyModel->roleForColumn(idx.column())) + { + case detail::BlockPropertiesResetRole: { - case detail::ColorRole: + auto stateWidget = internals.ProxyModel->getStateWidget(sourceIndex); + if (stateWidget->getResetButton()->isEnabled()) { - QColor color(255, 255, 255); - if (model->data(idx, detail::ColorRole).canConvert()) - { - color = model->data(idx, detail::ColorRole).value(); - } - color = QColorDialog::getColor( - color, this, "Select Color", QColorDialog::DontUseNativeDialog); - if (color.isValid()) - { - model->setData(idx, color, detail::ColorRole); - } + stateWidget->getResetButton()->setDown(true); } break; - - case detail::OpacityRole: + } + default: + break; + } + }); + // hookup clicked to reset state. + QObject::connect(internals.Ui.hierarchy, &QTreeView::clicked, [&](const QModelIndex& idx) { + auto model = internals.Ui.hierarchy->model(); + auto sourceIndex = qobject_cast(model)->mapToSource(idx); + switch (internals.ProxyModel->roleForColumn(idx.column())) + { + case detail::BlockPropertiesResetRole: + { + auto stateWidget = internals.ProxyModel->getStateWidget(sourceIndex); + if (stateWidget->getResetButton()->isEnabled()) { - // provide non-zero default when data is unset. - double opacity = 1.0; - if (model->data(idx, detail::OpacityRole).canConvert()) - { - opacity = model->data(idx, detail::OpacityRole).toDouble(); - } - pqDoubleRangeDialog dialog("Opacity:", 0.0, 1.0, this); - dialog.setObjectName("OpacityDialog"); - dialog.setWindowTitle(tr("Select Opacity")); - dialog.setValue(opacity); - if (dialog.exec() == QDialog::Accepted) - { - model->setData(idx, qBound(0.0, dialog.value(), 1.0), detail::OpacityRole); - } + stateWidget->getResetButton()->setDown(false); + stateWidget->getResetButton()->click(); } break; } - }); - - // hookup cntrl+clip to clear color and opacity. - QObject::connect(internals.Ui.hierarchy, &QTreeView::clicked, [&](const QModelIndex& idx) { - if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) - { - return; + default: + break; } - auto model = internals.Ui.hierarchy->model(); - const int role = internals.ProxyModel->roleForColumn(idx.column()); - model->setData(idx.siblingAtColumn(0), QVariant(), role); }); } adjustHeader(internals.Ui.hierarchy->header()); - adjustHeader(internals.Ui.colors->horizontalHeader()); - adjustHeader(internals.Ui.opacities->horizontalHeader()); } //----------------------------------------------------------------------------- @@ -1310,50 +1141,6 @@ void pqDataAssemblyPropertyWidget::assemblyTreeModified(int role) } Q_EMIT this->selectorsChanged(); } - else if (role == detail::ColorRole) - { - const auto colors = internals.AssemblyTreeModel->data(detail::ColorRole); - internals.ColorsTableModel->setData(colors); - internals.Colors = colorsToVariantList(colors); - internals.CompositeIndexColors.clear(); - if (assembly != nullptr && internals.InCompositeIndicesMode) - { - // convert selector:colors map to composite-index:color map. - QList> idColors; - for (const auto& pair : colors) - { - auto ids = - vtkDataAssemblyUtilities::GetSelectedCompositeIds({ pair.first.toStdString() }, assembly); - Q_ASSERT(ids.size() == 1); - idColors.push_back(qMakePair(ids.front(), pair.second)); - } - internals.CompositeIndexColors = colorsToVariantList(idColors); - } - - Q_EMIT this->colorsChanged(); - } - else if (role == detail::OpacityRole) - { - const auto opacities = internals.AssemblyTreeModel->data(detail::OpacityRole); - internals.OpacitiesTableModel->setData(opacities); - internals.Opacities = opacitiesToVariantList(opacities); - internals.CompositeIndexOpacities.clear(); - if (assembly != nullptr && internals.InCompositeIndicesMode) - { - // convert selector:opacities map to composite-index:opacities map. - QList> idOpacities; - for (const auto& pair : opacities) - { - auto ids = - vtkDataAssemblyUtilities::GetSelectedCompositeIds({ pair.first.toStdString() }, assembly); - Q_ASSERT(ids.size() == 1); - idOpacities.push_back(qMakePair(ids.front(), pair.second)); - } - internals.CompositeIndexOpacities = opacitiesToVariantList(idOpacities); - } - - Q_EMIT this->opacitiesChanged(); - } } //----------------------------------------------------------------------------- @@ -1371,38 +1158,6 @@ void pqDataAssemblyPropertyWidget::selectorsTableModified() Q_EMIT this->selectorsChanged(); } -//----------------------------------------------------------------------------- -void pqDataAssemblyPropertyWidget::colorsTableModified() -{ - auto& internals = (*this->Internals); - if (internals.BlockUpdates) - { - return; - } - - QScopedValueRollback rollback(internals.BlockUpdates, true); - const auto& colors = internals.ColorsTableModel->values(); - internals.Colors = colorsToVariantList(colors); - internals.AssemblyTreeModel->setData(colors, detail::ColorRole); - Q_EMIT this->colorsChanged(); -} - -//----------------------------------------------------------------------------- -void pqDataAssemblyPropertyWidget::opacitiesTableModified() -{ - auto& internals = (*this->Internals); - if (internals.BlockUpdates) - { - return; - } - - QScopedValueRollback rollback(internals.BlockUpdates, true); - const auto& opacities = internals.OpacitiesTableModel->values(); - internals.Opacities = opacitiesToVariantList(opacities); - internals.AssemblyTreeModel->setData(opacities, detail::OpacityRole); - Q_EMIT this->opacitiesChanged(); -} - //----------------------------------------------------------------------------- void pqDataAssemblyPropertyWidget::updateDataAssembly(vtkObject* sender) { @@ -1432,15 +1187,12 @@ void pqDataAssemblyPropertyWidget::updateDataAssembly(vtkObject* sender) if (internals.InCompositeIndicesMode) { this->setCompositeIndices(internals.CompositeIndices); - this->setCompositeIndexColors(internals.CompositeIndexColors); - this->setCompositeIndexOpacities(internals.CompositeIndexOpacities); } else { this->setSelectors(internals.Selectors); - this->setSelectorColors(internals.Colors); - this->setSelectorOpacities(internals.Opacities); } + internals.ProxyModel->initializeStateWidgets(assembly); auto domain = vtkSMDomain::SafeDownCast(sender); if (internals.UseInputNameAsHeader && domain) @@ -1517,104 +1269,6 @@ QList pqDataAssemblyPropertyWidget::compositeIndicesAsVariantList() co return internals.CompositeIndices; } -//----------------------------------------------------------------------------- -void pqDataAssemblyPropertyWidget::setCompositeIndexColors(const QList& values) -{ - auto& internals = (*this->Internals); - internals.CompositeIndexColors = values; - - QList cidColorSelectors; - for (int cc = 0; (cc + 3) < values.size(); cc += 4) - { - cidColorSelectors.push_back(QString("//*[@cid=%1]").arg(values[cc].value())); - cidColorSelectors.push_back(values[cc + 1]); - cidColorSelectors.push_back(values[cc + 2]); - cidColorSelectors.push_back(values[cc + 3]); - } - this->setSelectorColors(cidColorSelectors); -} - -//----------------------------------------------------------------------------- -QList pqDataAssemblyPropertyWidget::compositeIndexColorsAsVariantList() const -{ - const auto& internals = (*this->Internals); - return internals.CompositeIndexColors; -} - -//----------------------------------------------------------------------------- -void pqDataAssemblyPropertyWidget::setSelectorColors(const QList& values) -{ - auto& internals = (*this->Internals); - internals.Colors = values; - - QList> colors; - for (int cc = 0, max = values.size(); (cc + 3) < max; cc += 4) - { - const auto color = QColor::fromRgbF( - values[cc + 1].toDouble(), values[cc + 2].toDouble(), values[cc + 3].toDouble()); - colors.push_back(qMakePair(values[cc].toString(), color)); - } - - QScopedValueRollback rollback(internals.BlockUpdates, true); - internals.AssemblyTreeModel->setData(colors, detail::ColorRole); - internals.ColorsTableModel->setData(colors); - Q_EMIT this->colorsChanged(); -} - -//----------------------------------------------------------------------------- -QList pqDataAssemblyPropertyWidget::selectorColorsAsVariantList() const -{ - const auto& internals = (*this->Internals); - return internals.Colors; -} - -//----------------------------------------------------------------------------- -void pqDataAssemblyPropertyWidget::setCompositeIndexOpacities(const QList& values) -{ - auto& internals = (*this->Internals); - internals.CompositeIndexOpacities = values; - - QList cidOpacitySelectors; - for (int cc = 0; (cc + 1) < values.size(); cc += 2) - { - cidOpacitySelectors.push_back(QString("//*[@cid=%1]").arg(values[cc].value())); - cidOpacitySelectors.push_back(values[cc + 1]); - } - this->setSelectorOpacities(cidOpacitySelectors); -} - -//----------------------------------------------------------------------------- -QList pqDataAssemblyPropertyWidget::compositeIndexOpacitiesAsVariantList() const -{ - const auto& internals = (*this->Internals); - return internals.CompositeIndexOpacities; -} - -//----------------------------------------------------------------------------- -void pqDataAssemblyPropertyWidget::setSelectorOpacities(const QList& values) -{ - auto& internals = (*this->Internals); - internals.Opacities = values; - - QList> opacities; - for (int cc = 0, max = values.size(); (cc + 1) < max; cc += 2) - { - opacities.push_back(qMakePair(values[cc].toString(), values[cc + 1].toDouble())); - } - - QScopedValueRollback rollback(internals.BlockUpdates, true); - internals.AssemblyTreeModel->setData(opacities, detail::OpacityRole); - internals.OpacitiesTableModel->setData(opacities); - Q_EMIT this->opacitiesChanged(); -} - -//----------------------------------------------------------------------------- -QList pqDataAssemblyPropertyWidget::selectorOpacitiesAsVariantList() const -{ - const auto& internals = (*this->Internals); - return internals.Opacities; -} - //----------------------------------------------------------------------------- void pqDataAssemblyPropertyWidget::updateWidget(bool showing_advanced_properties) { diff --git a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h index 3dfbe1e4133..886ebcb62b6 100644 --- a/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h +++ b/Qt/ApplicationComponents/pqDataAssemblyPropertyWidget.h @@ -7,6 +7,8 @@ #include "pqApplicationComponentsModule.h" #include "pqPropertyWidget.h" +#include "vtkParaViewDeprecation.h" // for PARAVIEW_DEPRECATED + #include // for QScopedPointer /** @@ -16,13 +18,12 @@ * pqDataAssemblyPropertyWidget is intended for properties that rely on a * vtkDataAssembly i.e. use a vtkSMDataAssemblyDomain. This * supports getting/setting the list of selectors for checked nodes based on the - * chosen vtkDataAssembly. Further more, it supports editing color and opacity, - * if requested. + * chosen vtkDataAssembly. Further more, it supports show the state and resetting + * block properties. * * pqDataAssemblyPropertyWidget can be used on a single property with a * vtkSMDataAssemblyDomain or for a group of properties. For a single property, - * it allows for editing on selectors for checked nodes. For a group, it can - * support opacity and color editing as well. + * it allows for editing on selectors for checked nodes. * * Here's an example proxy XML for a single property. * @@ -45,12 +46,11 @@ * The widget will use the assembly provided by * `vtkSMDataAssemblyDomain::GetDataAssembly` to render a tree in UI. * - * A property-group for editing color and opacity, along with choosing which - * named-assembly to use is as follows. All properties in the group are optional - * and one may specify on subset that is relevant for their use-case. It is - * assumed, however, that all properties in the group use the same data - * assembly. If that's not the case, one should use separate property groups, - * hence separate widgets, for each. + * A property-group for choosing which named-assembly to use is as follows. + * All properties in the group are optional and one may specify on subset + * that is relevant for their use-case. It is assumed, however, that all properties + * in the group use the same data assembly. If that's not the case, one should use + * separate property groups, hence separate widgets, for each. * * @code{xml} * @@ -107,36 +107,10 @@ * * * - * - * - * - * - * - * - * - * - - * - * - * - * - * - * - * - * - * * * * * - * - * * * * @endcode @@ -148,7 +122,7 @@ * all properties in the group to consistently use vtkSMDataAssemblyDomain or * vtkSMCompositeTreeDomain and mixing is not allowed. * - * TODO: Remove the usage of vvtkSMCompositeTreeDomain. + * TODO: Remove the usage of vtkSMCompositeTreeDomain. * * @section Hints Hints * @@ -185,36 +159,14 @@ class PQAPPLICATIONCOMPONENTS_EXPORT pqDataAssemblyPropertyWidget : public pqPro Q_OBJECT typedef pqPropertyWidget Superclass; + ///@{ /** - * Property with selectors for checked nodes in the hierarchy. + * Property with selectors/composite indices for checked nodes in the hierarchy. */ Q_PROPERTY(QList selectors READ selectorsAsVariantList WRITE setSelectors NOTIFY selectorsChanged); - - /** - * Property with selectors and associated colors (as RGB). - */ - Q_PROPERTY(QList selectorColors READ selectorColorsAsVariantList WRITE setSelectorColors - NOTIFY colorsChanged); - - /** - * Property with selectors and associated opacities. - */ - Q_PROPERTY(QList selectorOpacities READ selectorOpacitiesAsVariantList WRITE - setSelectorOpacities NOTIFY opacitiesChanged); - - ///@{ - /** - * These are similar to the selector-based variants, except, instead of a - * selector, the composite index is used. This is used when supported - * vtkSMCompositeTreeDomain-based properties. - */ Q_PROPERTY(QList compositeIndices READ compositeIndicesAsVariantList WRITE setCompositeIndices NOTIFY selectorsChanged); - Q_PROPERTY(QList compositeIndexOpacities READ compositeIndexOpacitiesAsVariantList WRITE - setCompositeIndexOpacities NOTIFY opacitiesChanged); - Q_PROPERTY(QList compositeIndexColors READ compositeIndexColorsAsVariantList WRITE - setCompositeIndexColors NOTIFY colorsChanged); ///@} public: pqDataAssemblyPropertyWidget( @@ -247,11 +199,15 @@ public: * followed by corresponding RGB color or list of composite indices followed by * the color. */ - void setCompositeIndexColors(const QList& values); - QList compositeIndexColorsAsVariantList() const; - - void setSelectorColors(const QList& values); - QList selectorColorsAsVariantList() const; + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + void setCompositeIndexColors(const QList& values) { Q_UNUSED(values); } + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + QList compositeIndexColorsAsVariantList() const { return QList(); } + + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + void setSelectorColors(const QList& values) { Q_UNUSED(values); } + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + QList selectorColorsAsVariantList() const { return QList(); } ///@} ///@{ @@ -260,26 +216,30 @@ public: * followed by corresponding opacity or list of composite indices followed by * the opacity. */ - void setCompositeIndexOpacities(const QList& values); - QList compositeIndexOpacitiesAsVariantList() const; - - void setSelectorOpacities(const QList& values); - QList selectorOpacitiesAsVariantList() const; + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + void setCompositeIndexOpacities(const QList& values) { Q_UNUSED(values); } + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + QList compositeIndexOpacitiesAsVariantList() const { return QList(); } + + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + void setSelectorOpacities(const QList& values) { Q_UNUSED(values); } + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") + QList selectorOpacitiesAsVariantList() const { return QList(); } ///@} void updateWidget(bool showing_advanced_properties) override; Q_SIGNALS: void selectorsChanged(); + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") void colorsChanged(); + PARAVIEW_DEPRECATED_IN_5_13_0("No longer used.") void opacitiesChanged(); private Q_SLOTS: void updateDataAssembly(vtkObject* sender); void assemblyTreeModified(int role); void selectorsTableModified(); - void colorsTableModified(); - void opacitiesTableModified(); private: // NOLINT(readability-redundant-access-specifiers) Q_DISABLE_COPY(pqDataAssemblyPropertyWidget); diff --git a/Qt/Components/pqDataAssemblyTreeModel.cxx b/Qt/Components/pqDataAssemblyTreeModel.cxx index 7899dac208d..bed48d04ca6 100644 --- a/Qt/Components/pqDataAssemblyTreeModel.cxx +++ b/Qt/Components/pqDataAssemblyTreeModel.cxx @@ -281,7 +281,7 @@ pqDataAssemblyTreeModel::pqDataAssemblyTreeModel(QObject* parentObject) , Internals(new pqDataAssemblyTreeModel::pqInternals()) , UserCheckable(false) { - // set default to propagage checkstate change to the entire subtree. + // set default to propagate check state change to the entire subtree. this->setRoleProperty(Qt::CheckStateRole, pqDataAssemblyTreeModel::Inherited); } @@ -447,6 +447,10 @@ bool pqDataAssemblyTreeModel::setData(const QModelIndex& indx, const QVariant& v { auto& internals = (*this->Internals); const auto assembly = internals.DataAssembly.GetPointer(); + if (indx.column() != 0) + { + return false; + } const auto node = ::getNodeID(indx); if (node == -1 || assembly == nullptr) { -- GitLab From 86c3b951d9f25bcc03210b4a53f4f926a5494538 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 17 May 2024 18:17:57 -0400 Subject: [PATCH 26/43] pqUseSeparateOpacityArrayReaction: Refactoring --- .../pqUseSeparateOpacityArrayReaction.cxx | 83 +++++++++++-------- .../pqUseSeparateOpacityArrayReaction.h | 5 +- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.cxx b/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.cxx index f36b46c47e0..a8337b3369f 100644 --- a/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.cxx +++ b/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.cxx @@ -17,57 +17,78 @@ pqUseSeparateOpacityArrayReaction::pqUseSeparateOpacityArrayReaction( , TrackActiveObjects(track_active_objects) { parentObject->setCheckable(true); - QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), this, SLOT(updateEnableState()), - Qt::QueuedConnection); - this->updateEnableState(); + if (track_active_objects) + { + QObject::connect(&pqActiveObjects::instance(), + QOverload::of(&pqActiveObjects::representationChanged), this, + &pqUseSeparateOpacityArrayReaction::setActiveRepresentation, Qt::QueuedConnection); + this->setActiveRepresentation(); + } + else + { + this->updateEnableState(); + } } //----------------------------------------------------------------------------- pqUseSeparateOpacityArrayReaction::~pqUseSeparateOpacityArrayReaction() = default; //----------------------------------------------------------------------------- -void pqUseSeparateOpacityArrayReaction::updateEnableState() +pqDataRepresentation* pqUseSeparateOpacityArrayReaction::representation() const { - pqDataRepresentation* cachedRepr = this->CachedRepresentation; - this->setRepresentation( - this->TrackActiveObjects ? pqActiveObjects::instance().activeRepresentation() : cachedRepr); + return this->Representation; } //----------------------------------------------------------------------------- -pqDataRepresentation* pqUseSeparateOpacityArrayReaction::representation() const +void pqUseSeparateOpacityArrayReaction::setActiveRepresentation() { - return this->CachedRepresentation; + this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); } //----------------------------------------------------------------------------- void pqUseSeparateOpacityArrayReaction::setRepresentation(pqDataRepresentation* repr) { + if (this->Representation != nullptr && this->Representation == repr) + { + return; + } this->Links.clear(); - if (this->CachedRepresentation) + if (this->Representation) { - QObject::disconnect(this->CachedRepresentation, SIGNAL(colorArrayNameModified()), this, - SLOT(updateEnableState())); - QObject::disconnect(this->CachedRepresentation, SIGNAL(representationTypeModified()), this, - SLOT(updateEnableState())); - QObject::disconnect( - this->CachedRepresentation, SIGNAL(useTransfer2DModified()), this, SLOT(updateEnableState())); + this->disconnect(this->Representation); + this->Representation = nullptr; } - this->CachedRepresentation = repr; if (repr) { - QObject::connect(repr, SIGNAL(colorArrayNameModified()), this, SLOT(updateEnableState()), - Qt::QueuedConnection); - QObject::connect(repr, SIGNAL(representationTypeModified()), this, SLOT(updateEnableState()), - Qt::QueuedConnection); - QObject::connect( - repr, SIGNAL(useTransfer2DModified()), this, SLOT(updateEnableState()), Qt::QueuedConnection); + this->Representation = repr; + QObject::connect(repr, &pqDataRepresentation::colorArrayNameModified, this, + &pqUseSeparateOpacityArrayReaction::updateEnableState, Qt::QueuedConnection); + QObject::connect(repr, &pqDataRepresentation::representationTypeModified, this, + &pqUseSeparateOpacityArrayReaction::updateEnableState, Qt::QueuedConnection); + QObject::connect(repr, &pqDataRepresentation::useTransfer2DModified, this, + &pqUseSeparateOpacityArrayReaction::updateEnableState, Qt::QueuedConnection); } + this->updateEnableState(); + // Recover proxy and action - vtkSMProxy* reprProxy = repr ? repr->getProxy() : nullptr; - QAction* action = this->parentAction(); + vtkSMProxy* reprProxy = this->Representation ? this->Representation->getProxy() : nullptr; + vtkSMProperty* useSepOpacityProp = + reprProxy ? reprProxy->GetProperty("UseSeparateOpacityArray") : nullptr; + // add link between action and property + if (useSepOpacityProp && reprProxy) + { + this->Links.addPropertyLink( + this->parentAction(), "checked", SIGNAL(toggled(bool)), reprProxy, useSepOpacityProp); + } +} +//----------------------------------------------------------------------------- +void pqUseSeparateOpacityArrayReaction::updateEnableState() +{ + // Recover proxy and action + vtkSMProxy* reprProxy = this->Representation ? this->Representation->getProxy() : nullptr; + QAction* action = this->parentAction(); // Set action state vtkSMProperty* useSepOpacityProp = reprProxy ? reprProxy->GetProperty("UseSeparateOpacityArray") : nullptr; @@ -75,16 +96,12 @@ void pqUseSeparateOpacityArrayReaction::setRepresentation(pqDataRepresentation* // Do not allow usage of a separate opacity array when the representation is // using a 2D transfer function. - bool usingTf2D = useTf2DProperty ? vtkSMPropertyHelper(useTf2DProperty).GetAsInt() == 1 : false; - bool canUseSepOpacityArray = usingTf2D + const bool usingTf2D = + useTf2DProperty ? vtkSMPropertyHelper(useTf2DProperty).GetAsInt() == 1 : false; + const bool canUseSepOpacityArray = usingTf2D ? false : (reprProxy && useSepOpacityProp && vtkSMRepresentationProxy::IsVolumeRendering(reprProxy) && vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)); action->setEnabled(canUseSepOpacityArray); action->setChecked(false); - if (useSepOpacityProp && reprProxy) - { - this->Links.addPropertyLink( - action, "checked", SIGNAL(toggled(bool)), reprProxy, useSepOpacityProp); - } } diff --git a/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.h b/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.h index af772a544eb..cae8d8f50e1 100644 --- a/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.h +++ b/Qt/ApplicationComponents/pqUseSeparateOpacityArrayReaction.h @@ -34,10 +34,13 @@ public: pqDataRepresentation* representation() const; public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) + ///@{ /** * Set the active representation. */ void setRepresentation(pqDataRepresentation*); + void setActiveRepresentation(); + ///@} protected Q_SLOTS: /** @@ -49,7 +52,7 @@ private: Q_DISABLE_COPY(pqUseSeparateOpacityArrayReaction) pqPropertyLinks Links; - QPointer CachedRepresentation; + QPointer Representation; bool TrackActiveObjects; }; -- GitLab From 0a10086033eae19400f5d20cd7b82e6b7df1f247 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 17 May 2024 18:23:13 -0400 Subject: [PATCH 27/43] pqUse2DTransferFunctionReaction: Refactoring --- .../pqUse2DTransferFunctionReaction.cxx | 79 +++++++++++-------- .../pqUse2DTransferFunctionReaction.h | 5 +- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.cxx b/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.cxx index 73f7f8d0972..9fb968ef1cc 100644 --- a/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.cxx +++ b/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.cxx @@ -17,59 +17,81 @@ pqUse2DTransferFunctionReaction::pqUse2DTransferFunctionReaction( , TrackActiveObjects(track_active_objects) { parentObject->setCheckable(true); - QObject::connect(&pqActiveObjects::instance(), - SIGNAL(representationChanged(pqDataRepresentation*)), this, SLOT(updateEnableState()), - Qt::QueuedConnection); - this->updateEnableState(); + if (track_active_objects) + { + QObject::connect(&pqActiveObjects::instance(), + QOverload::of(&pqActiveObjects::representationChanged), this, + &pqUse2DTransferFunctionReaction::setActiveRepresentation, Qt::QueuedConnection); + this->setActiveRepresentation(); + } + else + { + this->updateEnableState(); + } } //----------------------------------------------------------------------------- pqUse2DTransferFunctionReaction::~pqUse2DTransferFunctionReaction() = default; //----------------------------------------------------------------------------- -void pqUse2DTransferFunctionReaction::updateEnableState() +pqDataRepresentation* pqUse2DTransferFunctionReaction::representation() const { - pqDataRepresentation* cachedRepr = this->CachedRepresentation; - this->setRepresentation( - this->TrackActiveObjects ? pqActiveObjects::instance().activeRepresentation() : cachedRepr); + return this->Representation; } //----------------------------------------------------------------------------- -pqDataRepresentation* pqUse2DTransferFunctionReaction::representation() const +void pqUse2DTransferFunctionReaction::setActiveRepresentation() { - return this->CachedRepresentation; + this->setRepresentation(pqActiveObjects::instance().activeRepresentation()); } //----------------------------------------------------------------------------- void pqUse2DTransferFunctionReaction::setRepresentation(pqDataRepresentation* repr) { + if (this->Representation != nullptr && this->Representation == repr) + { + return; + } this->Links.clear(); - if (this->CachedRepresentation) + if (this->Representation) { - QObject::disconnect(this->CachedRepresentation, SIGNAL(colorArrayNameModified()), this, - SLOT(updateEnableState())); - QObject::disconnect(this->CachedRepresentation, SIGNAL(representationTypeModified()), this, - SLOT(updateEnableState())); - QObject::disconnect(this->CachedRepresentation, SIGNAL(useSeparateOpacityArrayModified()), this, - SLOT(updateEnableState())); + this->disconnect(this->Representation); + this->Representation = nullptr; } - this->CachedRepresentation = repr; if (repr) { + this->Representation = repr; // Observing `colorArrayNameModified` lets this reaction disable its parent action // when the representation is not using scalar coloring. // For example, when the color array combo box is set to "Solid Color". - QObject::connect(repr, SIGNAL(colorArrayNameModified()), this, SLOT(updateEnableState()), - Qt::QueuedConnection); - QObject::connect(repr, SIGNAL(representationTypeModified()), this, SLOT(updateEnableState()), - Qt::QueuedConnection); - QObject::connect(repr, SIGNAL(useSeparateOpacityArrayModified()), this, - SLOT(updateEnableState()), Qt::QueuedConnection); + QObject::connect(repr, &pqDataRepresentation::colorArrayNameModified, this, + &pqUse2DTransferFunctionReaction::updateEnableState, Qt::QueuedConnection); + QObject::connect(repr, &pqDataRepresentation::representationTypeModified, this, + &pqUse2DTransferFunctionReaction::updateEnableState, Qt::QueuedConnection); + QObject::connect(repr, &pqDataRepresentation::useSeparateOpacityArrayModified, this, + &pqUse2DTransferFunctionReaction::updateEnableState, Qt::QueuedConnection); } + this->updateEnableState(); + // Recover proxy and action vtkSMProxy* reprProxy = repr ? repr->getProxy() : nullptr; QAction* action = this->parentAction(); + vtkSMProperty* useTf2DProperty = reprProxy ? reprProxy->GetProperty("UseTransfer2D") : nullptr; + // add link between action and property + if (useTf2DProperty && reprProxy) + { + this->Links.addPropertyLink( + action, "checked", SIGNAL(toggled(bool)), reprProxy, useTf2DProperty); + } +} + +//----------------------------------------------------------------------------- +void pqUse2DTransferFunctionReaction::updateEnableState() +{ + // Recover proxy and action + vtkSMProxy* reprProxy = this->Representation ? this->Representation->getProxy() : nullptr; + QAction* action = this->parentAction(); // Set action state vtkSMProperty* uoaProperty = @@ -77,18 +99,13 @@ void pqUse2DTransferFunctionReaction::setRepresentation(pqDataRepresentation* re // Do not allow usage of a 2D transfer function when the representation is already using // a separate array to map opacity to the scalar opacity function. - bool usingSepOpacityArray = + const bool usingSepOpacityArray = reprProxy && uoaProperty && vtkSMPropertyHelper(uoaProperty).GetAsInt() == 1; vtkSMProperty* useTf2DProperty = reprProxy ? reprProxy->GetProperty("UseTransfer2D") : nullptr; - bool canUseTf2D = usingSepOpacityArray + const bool canUseTf2D = usingSepOpacityArray ? false : (reprProxy && useTf2DProperty && vtkSMRepresentationProxy::IsVolumeRendering(reprProxy) && vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)); action->setEnabled(canUseTf2D); action->setChecked(false); - if (useTf2DProperty && reprProxy) - { - this->Links.addPropertyLink( - action, "checked", SIGNAL(toggled(bool)), reprProxy, useTf2DProperty); - } } diff --git a/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.h b/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.h index b467d348578..ccfca557983 100644 --- a/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.h +++ b/Qt/ApplicationComponents/pqUse2DTransferFunctionReaction.h @@ -34,10 +34,13 @@ public: pqDataRepresentation* representation() const; public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) + ///@{ /** * Set the active representation. */ void setRepresentation(pqDataRepresentation*); + void setActiveRepresentation(); + ///@} protected Q_SLOTS: /** @@ -49,7 +52,7 @@ private: Q_DISABLE_COPY(pqUse2DTransferFunctionReaction) pqPropertyLinks Links; - QPointer CachedRepresentation; + QPointer Representation; bool TrackActiveObjects; }; -- GitLab From 4b8c4fa194d8bf08779a5d05f970103b4eff9c37 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 17 May 2024 18:50:57 -0400 Subject: [PATCH 28/43] pqColorMapEditor: Support block(s) coloring --- .../Resources/UI/pqColorMapEditor.ui | 31 +- Qt/ApplicationComponents/pqColorMapEditor.cxx | 464 +++++++++++------- Qt/ApplicationComponents/pqColorMapEditor.h | 31 +- .../pqEditColorMapReaction.cxx | 14 +- 4 files changed, 350 insertions(+), 190 deletions(-) diff --git a/Qt/ApplicationComponents/Resources/UI/pqColorMapEditor.ui b/Qt/ApplicationComponents/Resources/UI/pqColorMapEditor.ui index cb58fee9edf..d357a150bd3 100644 --- a/Qt/ApplicationComponents/Resources/UI/pqColorMapEditor.ui +++ b/Qt/ApplicationComponents/Resources/UI/pqColorMapEditor.ui @@ -100,27 +100,34 @@ + + + Selected Properties + + + + Color - + Color Y - + Opacity - + Use separate color map @@ -143,7 +150,7 @@ - + Use a 2D transfer function. Available only for volume rendering. @@ -163,7 +170,7 @@ - + Use a separate array to map opacity. Available only for volume rendering. @@ -187,20 +194,30 @@ + + + + 0 + 0 + + + + + Select array used to map color. - + Select array used to map to the Y-Axis of a 2D transfer function. - + Select array used to map opacity. diff --git a/Qt/ApplicationComponents/pqColorMapEditor.cxx b/Qt/ApplicationComponents/pqColorMapEditor.cxx index f6a70300598..3ddbb5ed898 100644 --- a/Qt/ApplicationComponents/pqColorMapEditor.cxx +++ b/Qt/ApplicationComponents/pqColorMapEditor.cxx @@ -8,9 +8,7 @@ #include "pqApplicationCore.h" #include "pqDataRepresentation.h" #include "pqEditScalarBarReaction.h" -#include "pqPipelineSource.h" #include "pqProxyWidget.h" -#include "pqProxyWidgetDialog.h" #include "pqSMAdaptor.h" #include "pqScalarBarVisibilityReaction.h" #include "pqSearchBox.h" @@ -21,36 +19,52 @@ #include "pqUseSeparateOpacityArrayReaction.h" #include "vtkCommand.h" -#include "vtkPVArrayInformation.h" +#include "vtkNew.h" #include "vtkSMColorMapEditorHelper.h" #include "vtkSMCoreUtilities.h" #include "vtkSMProperty.h" #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" +#include "vtkSMSessionProxyManager.h" #include "vtkSMSettings.h" #include "vtkSMTransferFunctionProxy.h" -#include "vtkWeakPointer.h" +#include "vtkSmartPointer.h" -#include - -#include +#include #include #include -#include +#include #include +#include + +#include +#include +#include +#include class pqColorMapEditor::pqInternals { public: Ui::ColorMapEditor Ui; QPointer ProxyWidget; - QPointer ActiveRepresentation; + QPointer Representation; + + QPointer ScalarBarVisibilityReaction; + QPointer EditScalarBarReaction; + QPointer UseSeparateColorMapReaction; + QPointer UseSeparateOpacityArrayReaction; + QPointer UseTransfer2DReaction; + QPointer ScalarBarVisibilityAction; QPointer EditScalarBarAction; QPointer UseSeparateColorArrayAction; QPointer UseSeparateOpacityArrayAction; QPointer UseTransfer2DAction; + vtkNew ColorMapEditorHelper; + std::vector CTFs; + vtkSmartPointer CopyFirstCTF; + unsigned long ObserverId; pqInternals(pqColorMapEditor* self) @@ -71,71 +85,87 @@ pqColorMapEditor::pqColorMapEditor(QWidget* parentObject) : Superclass(parentObject) , Internals(new pqColorMapEditor::pqInternals(this)) { - QObject::connect(this->Internals->Ui.SearchBox, SIGNAL(advancedSearchActivated(bool)), this, - SLOT(updatePanel())); - QObject::connect( - this->Internals->Ui.SearchBox, SIGNAL(textChanged(QString)), this, SLOT(updatePanel())); - QObject::connect( - this->Internals->Ui.RestoreDefaults, SIGNAL(clicked()), this, SLOT(restoreDefaults())); + QObject::connect(this->Internals->Ui.SearchBox, &pqSearchBox::advancedSearchActivated, this, + &pqColorMapEditor::updatePanel); QObject::connect( - this->Internals->Ui.SaveAsDefaults, SIGNAL(clicked()), this, SLOT(saveAsDefault())); + this->Internals->Ui.SearchBox, &pqSearchBox::textChanged, this, &pqColorMapEditor::updatePanel); + QObject::connect(this->Internals->Ui.RestoreDefaults, &QPushButton::clicked, this, + &pqColorMapEditor::restoreDefaults); + QObject::connect(this->Internals->Ui.SaveAsDefaults, &QPushButton::clicked, this, + &pqColorMapEditor::saveAsDefault); + QObject::connect(this->Internals->Ui.SaveAsArrayDefaults, &QPushButton::clicked, this, + &pqColorMapEditor::saveAsArrayDefault); QObject::connect( - this->Internals->Ui.SaveAsArrayDefaults, SIGNAL(clicked()), this, SLOT(saveAsArrayDefault())); + this->Internals->Ui.AutoUpdate, &QPushButton::clicked, this, &pqColorMapEditor::setAutoUpdate); QObject::connect( - this->Internals->Ui.AutoUpdate, SIGNAL(clicked(bool)), this, SLOT(setAutoUpdate(bool))); - QObject::connect(this->Internals->Ui.Update, SIGNAL(clicked()), this, SLOT(renderViews())); + this->Internals->Ui.Update, &QPushButton::clicked, this, &pqColorMapEditor::renderViews); // Let reactions do the heavy lifting for managing the enabled state of the tool buttons. this->Internals->ScalarBarVisibilityAction = new QAction(this); - this->Internals->Ui.ShowScalarBar->connect( - this->Internals->ScalarBarVisibilityAction, SIGNAL(toggled(bool)), SLOT(setChecked(bool))); - this->Internals->ScalarBarVisibilityAction->connect( - this->Internals->Ui.ShowScalarBar, SIGNAL(clicked()), SLOT(trigger())); - new pqScalarBarVisibilityReaction(this->Internals->ScalarBarVisibilityAction); + + QObject::connect(this->Internals->ScalarBarVisibilityAction, &QAction::toggled, + this->Internals->Ui.ShowScalarBar, &QToolButton::setChecked); + QObject::connect(this->Internals->Ui.ShowScalarBar, &QToolButton::clicked, + this->Internals->ScalarBarVisibilityAction, &QAction::trigger); + this->Internals->ScalarBarVisibilityReaction = new pqScalarBarVisibilityReaction( + this->Internals->ScalarBarVisibilityAction, /*track_active_objects=*/false); this->Internals->EditScalarBarAction = new QAction(this); - this->Internals->EditScalarBarAction->connect( - this->Internals->Ui.EditScalarBar, SIGNAL(clicked()), SLOT(trigger())); - new pqEditScalarBarReaction(this->Internals->EditScalarBarAction); + QObject::connect(this->Internals->Ui.EditScalarBar, &QToolButton::clicked, + this->Internals->EditScalarBarAction, &QAction::trigger); + this->Internals->EditScalarBarReaction = new pqEditScalarBarReaction( + this->Internals->EditScalarBarAction, /*track_active_objects=*/false); + this->Internals->EditScalarBarReaction->setScalarBarVisibilityReaction( + this->Internals->ScalarBarVisibilityReaction); this->Internals->UseSeparateColorArrayAction = new QAction(this); - this->Internals->UseSeparateColorArrayAction->connect( - this->Internals->Ui.UseSeparateColorArray, SIGNAL(clicked()), SLOT(trigger())); + QObject::connect(this->Internals->Ui.UseSeparateColorArray, &QToolButton::clicked, + this->Internals->UseSeparateColorArrayAction, &QAction::trigger); QObject::connect(this->Internals->UseSeparateColorArrayAction, &QAction::changed, this, &pqColorMapEditor::updateColorArraySelectorWidgets); this->updateColorArraySelectorWidgets(); - new pqUseSeparateColorMapReaction( - this->Internals->UseSeparateColorArrayAction, this->Internals->Ui.DisplayColorWidget); + this->Internals->UseSeparateColorMapReaction = + new pqUseSeparateColorMapReaction(this->Internals->UseSeparateColorArrayAction, + this->Internals->Ui.DisplayColorWidget, /*track_active_objects=*/false); this->Internals->UseSeparateOpacityArrayAction = new QAction(this); - this->Internals->UseSeparateOpacityArrayAction->connect( - this->Internals->Ui.UseSeparateOpacityArray, SIGNAL(clicked()), SLOT(trigger())); + QObject::connect(this->Internals->Ui.UseSeparateOpacityArray, &QToolButton::clicked, + this->Internals->UseSeparateOpacityArrayAction, &QAction::trigger); QObject::connect(this->Internals->UseSeparateOpacityArrayAction, &QAction::changed, this, &pqColorMapEditor::updateOpacityArraySelectorWidgets); this->updateOpacityArraySelectorWidgets(); - new pqUseSeparateOpacityArrayReaction(this->Internals->UseSeparateOpacityArrayAction); + this->Internals->UseSeparateOpacityArrayReaction = new pqUseSeparateOpacityArrayReaction( + this->Internals->UseSeparateOpacityArrayAction, /*track_active_objects=*/false); this->Internals->UseTransfer2DAction = new QAction(this); - this->Internals->UseTransfer2DAction->connect( - this->Internals->Ui.Use2DTransferFunction, SIGNAL(clicked()), SLOT(trigger())); + QObject::connect(this->Internals->Ui.Use2DTransferFunction, &QToolButton::clicked, + this->Internals->UseTransfer2DAction, &QAction::trigger); QObject::connect(this->Internals->UseTransfer2DAction, &QAction::changed, this, &pqColorMapEditor::updateColor2ArraySelectorWidgets); this->updateColor2ArraySelectorWidgets(); - new pqUse2DTransferFunctionReaction(this->Internals->UseTransfer2DAction); + this->Internals->UseTransfer2DReaction = + new pqUse2DTransferFunctionReaction(this->Internals->UseTransfer2DAction, + /*track_active_objects=*/false); // update the enable state for the buttons based on the actions. - this->connect( - this->Internals->ScalarBarVisibilityAction, SIGNAL(changed()), SLOT(updateScalarBarButtons())); - this->connect( - this->Internals->EditScalarBarAction, SIGNAL(changed()), SLOT(updateScalarBarButtons())); + QObject::connect(this->Internals->ScalarBarVisibilityAction, &QAction::changed, this, + &pqColorMapEditor::updateScalarBarButtons); + QObject::connect(this->Internals->EditScalarBarAction, &QAction::changed, this, + &pqColorMapEditor::updateScalarBarButtons); this->updateScalarBarButtons(); - pqActiveObjects* activeObjects = &pqActiveObjects::instance(); - this->connect(activeObjects, SIGNAL(representationChanged(pqDataRepresentation*)), this, - SLOT(updateActive())); + auto selectedPropertiesComboBox = this->Internals->Ui.SelectedPropertiesComboBox; + selectedPropertiesComboBox->addItem(tr("Representation")); + selectedPropertiesComboBox->addItem(tr("Block(s)")); + selectedPropertiesComboBox->setCurrentIndex(0); + QObject::connect(selectedPropertiesComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &pqColorMapEditor::setSelectedPropertiesType); - pqSettings* settings = pqApplicationCore::instance()->settings(); - if (settings) + QObject::connect(&pqActiveObjects::instance(), + QOverload::of(&pqActiveObjects::representationChanged), this, + QOverload<>::of(&pqColorMapEditor::updateActive)); + + if (pqSettings* settings = pqApplicationCore::instance()->settings()) { this->Internals->Ui.AutoUpdate->setChecked( settings->value("autoUpdateColorMapEditor2", true).toBool()); @@ -143,6 +173,21 @@ pqColorMapEditor::pqColorMapEditor(QWidget* parentObject) this->updateActive(); } +//----------------------------------------------------------------------------- +void pqColorMapEditor::setSelectedPropertiesType(int selectedPropertiesType) +{ + const int oldSelectedPropertiesType = + this->Internals->ColorMapEditorHelper->GetSelectedPropertiesType(); + if (oldSelectedPropertiesType != selectedPropertiesType) + { + this->Internals->ColorMapEditorHelper->SetSelectedPropertiesType(selectedPropertiesType); + const bool prev = this->blockSignals(true); + this->Internals->Ui.SelectedPropertiesComboBox->setCurrentIndex(selectedPropertiesType); + this->blockSignals(prev); + this->updateActive(/*forceUpdate=*/true); + } +} + //----------------------------------------------------------------------------- pqColorMapEditor::~pqColorMapEditor() { @@ -152,9 +197,6 @@ pqColorMapEditor::~pqColorMapEditor() // save the state of advanced button in the user config. settings->setValue("autoUpdateColorMapEditor2", this->Internals->Ui.AutoUpdate->isChecked()); } - - delete this->Internals; - this->Internals = nullptr; } //----------------------------------------------------------------------------- @@ -169,44 +211,47 @@ void pqColorMapEditor::updatePanel() } //----------------------------------------------------------------------------- -void pqColorMapEditor::updateActive() +void pqColorMapEditor::updateActive(bool forceUpdate) { pqDataRepresentation* repr = pqActiveObjects::instance().activeRepresentation(); + this->setRepresentation(repr, forceUpdate); - this->setDataRepresentation(repr); - + auto colorMapEditorHelper = this->Internals->ColorMapEditorHelper.Get(); // Set the current LUT proxy to edit. - if (repr && vtkSMColorMapEditorHelper::GetUsingScalarColoring(repr->getProxy())) + if (repr && colorMapEditorHelper->GetAnySelectedUsingScalarColoring(repr->getProxy())) { - vtkSMProxy* lutProxy = vtkSMPropertyHelper(repr->getProxy(), "LookupTable", true).GetAsProxy(); - if (lutProxy) + auto luts = colorMapEditorHelper->GetSelectedLookupTables(repr->getProxy()); + for (vtkSMProxy* lutProxy : luts) { - // setup property links with the representation. - if (vtkSMProperty* useTF2DProperty = repr->getProxy()->GetProperty("UseTransfer2D")) + if (lutProxy) { - useTF2DProperty->AddLinkedProperty(lutProxy->GetProperty("Using2DTransferFunction")); + // setup property links with the representation. + if (vtkSMProperty* useTF2DProperty = repr->getProxy()->GetProperty("UseTransfer2D")) + { + useTF2DProperty->AddLinkedProperty(lutProxy->GetProperty("Using2DTransferFunction")); + } + else + { + vtkSMPropertyHelper(lutProxy, "Using2DTransferFunction").Set(0); + } + if (vtkSMProperty* useSepOpacityProperty = + repr->getProxy()->GetProperty("UseSeparateOpacityArray")) + { + useSepOpacityProperty->AddLinkedProperty( + lutProxy->GetProperty("UsingSeparateOpacityArray")); + } + else + { + vtkSMPropertyHelper(lutProxy, "UsingSeparateOpacityArray").Set(0); + } } - else + else if (lutProxy != nullptr) { vtkSMPropertyHelper(lutProxy, "Using2DTransferFunction").Set(0); - } - if (vtkSMProperty* useSepOpacityProperty = - repr->getProxy()->GetProperty("UseSeparateOpacityArray")) - { - useSepOpacityProperty->AddLinkedProperty( - lutProxy->GetProperty("UsingSeparateOpacityArray")); - } - else - { vtkSMPropertyHelper(lutProxy, "UsingSeparateOpacityArray").Set(0); } } - else if (lutProxy != nullptr) - { - vtkSMPropertyHelper(lutProxy, "Using2DTransferFunction").Set(0); - vtkSMPropertyHelper(lutProxy, "UsingSeparateOpacityArray").Set(0); - } - this->setColorTransferFunction(lutProxy); + this->setColorTransferFunctions(luts); } else { @@ -215,67 +260,75 @@ void pqColorMapEditor::updateActive() } //----------------------------------------------------------------------------- -void pqColorMapEditor::setDataRepresentation(pqDataRepresentation* repr) +void pqColorMapEditor::setRepresentation(pqDataRepresentation* repr, bool forceUpdate) { // this method sets up hooks to ensure that when the repr's properties are // modified, the editor shows the correct LUT. - if (this->Internals->ActiveRepresentation == repr) + if (this->Internals->Representation == repr && !forceUpdate) { return; } - if (this->Internals->ActiveRepresentation) + if (this->Internals->Representation) { // disconnect signals. if (this->Internals->ObserverId) { - this->Internals->ActiveRepresentation->getProxy()->RemoveObserver( - this->Internals->ObserverId); + this->Internals->Representation->getProxy()->RemoveObserver(this->Internals->ObserverId); } } this->Internals->ObserverId = 0; - this->Internals->ActiveRepresentation = repr; + this->Internals->Representation = repr; if (repr && repr->getProxy()) { this->Internals->ObserverId = repr->getProxy()->AddObserver( - vtkCommand::PropertyModifiedEvent, this, &pqColorMapEditor::updateActive); + vtkCommand::PropertyModifiedEvent, this, QOverload<>::of(&pqColorMapEditor::updateActive)); } - this->Internals->Ui.DisplayColorWidget->setRepresentation(repr); - this->Internals->Ui.DisplayOpacityWidget->setRepresentation(repr); - this->Internals->Ui.DisplayColor2Widget->setRepresentation(repr); + auto selectedPropertiesType = this->Internals->ColorMapEditorHelper->GetSelectedPropertiesType(); + this->Internals->Ui.DisplayColorWidget->setRepresentation(repr, selectedPropertiesType); + this->Internals->Ui.DisplayOpacityWidget->setRepresentation(repr); // no block support + this->Internals->Ui.DisplayColor2Widget->setRepresentation(repr); // no block support + this->Internals->ScalarBarVisibilityReaction->setRepresentation(repr, selectedPropertiesType); + this->Internals->EditScalarBarReaction->setRepresentation(repr, selectedPropertiesType); + this->Internals->UseSeparateColorMapReaction->setRepresentation(repr, selectedPropertiesType); + this->Internals->UseSeparateOpacityArrayReaction->setRepresentation(repr); // no block support + this->Internals->UseTransfer2DReaction->setRepresentation(repr); // no block support } //----------------------------------------------------------------------------- -void pqColorMapEditor::setColorTransferFunction(vtkSMProxy* ctf) +void pqColorMapEditor::setColorTransferFunctions(std::vector ctfs) { Ui::ColorMapEditor& ui = this->Internals->Ui; + vtkSMProxy* firstCTF = ctfs.empty() ? nullptr : ctfs[0]; - if (this->Internals->ProxyWidget == nullptr && ctf == nullptr) + if (this->Internals->ProxyWidget == nullptr && firstCTF == nullptr) { return; } - if (this->Internals->ProxyWidget && ctf && this->Internals->ProxyWidget->proxy() == ctf) + if (this->Internals->ProxyWidget && firstCTF && + this->Internals->ProxyWidget->proxy() == firstCTF && this->Internals->CTFs == ctfs) { return; } - if ((ctf == nullptr && this->Internals->ProxyWidget) || - (this->Internals->ProxyWidget && ctf && this->Internals->ProxyWidget->proxy() != ctf)) + if ((firstCTF == nullptr && this->Internals->ProxyWidget) || + (this->Internals->ProxyWidget && firstCTF && + (this->Internals->ProxyWidget->proxy() != firstCTF || this->Internals->CTFs != ctfs))) { ui.PropertiesFrame->layout()->removeWidget(this->Internals->ProxyWidget); delete this->Internals->ProxyWidget; } - ui.RestoreDefaults->setEnabled(ctf != nullptr); - ui.SaveAsDefaults->setEnabled(ctf != nullptr); - ui.SaveAsArrayDefaults->setEnabled(ctf != nullptr); - if (!ctf) + ui.RestoreDefaults->setEnabled(firstCTF != nullptr); + ui.SaveAsDefaults->setEnabled(firstCTF != nullptr); + ui.SaveAsArrayDefaults->setEnabled(firstCTF != nullptr); + if (!firstCTF) { return; } - pqProxyWidget* widget = new pqProxyWidget(ctf, this); + pqProxyWidget* widget = new pqProxyWidget(firstCTF, this); widget->setObjectName("Properties"); widget->setApplyChangesImmediately(true); widget->filterWidgets(); @@ -283,9 +336,14 @@ void pqColorMapEditor::setColorTransferFunction(vtkSMProxy* ctf) ui.PropertiesFrame->layout()->addWidget(widget); this->Internals->ProxyWidget = widget; + this->Internals->CTFs = ctfs; + this->Internals->CopyFirstCTF = + vtk::TakeSmartPointer(this->Internals->Representation->proxyManager()->NewProxy( + firstCTF->GetXMLGroup(), firstCTF->GetXMLName())); + this->Internals->CopyFirstCTF->Copy(firstCTF); this->updatePanel(); - QObject::connect(widget, SIGNAL(changeFinished()), this, SLOT(updateIfNeeded())); + QObject::connect(widget, &pqProxyWidget::changeFinished, this, &pqColorMapEditor::updateIfNeeded); } //----------------------------------------------------------------------------- @@ -303,10 +361,10 @@ void pqColorMapEditor::updateColorArraySelectorWidgets() pqInternals& internals = *this->Internals; Ui::ColorMapEditor& ui = internals.Ui; - bool enabled = internals.UseSeparateColorArrayAction->isEnabled(); + const bool enabled = internals.UseSeparateColorArrayAction->isEnabled(); ui.UseSeparateColorArray->setEnabled(enabled); - bool checked = internals.UseSeparateColorArrayAction->isChecked(); + const bool checked = internals.UseSeparateColorArrayAction->isChecked(); ui.UseSeparateColorArray->setChecked(checked); } @@ -316,10 +374,10 @@ void pqColorMapEditor::updateColor2ArraySelectorWidgets() pqInternals& internals = *this->Internals; Ui::ColorMapEditor& ui = internals.Ui; - bool enabled = internals.UseTransfer2DAction->isEnabled(); + const bool enabled = internals.UseTransfer2DAction->isEnabled(); ui.Use2DTransferFunction->setEnabled(enabled); - bool checked = internals.UseTransfer2DAction->isChecked(); + const bool checked = internals.UseTransfer2DAction->isChecked(); ui.Use2DTransferFunction->setChecked(checked); ui.Color2Label->setEnabled(checked && enabled); @@ -333,6 +391,11 @@ void pqColorMapEditor::updateColor2ArraySelectorWidgets() { ui.ColorLabel->setText(tr("Color")); } + + // when 2D transfer function is enabled, disable the option to use block properties + const int currentIndex = ui.SelectedPropertiesComboBox->currentIndex(); + this->Internals->Ui.SelectedPropertiesComboBox->setCurrentIndex(enabled ? 0 : currentIndex); + this->Internals->Ui.SelectedPropertiesComboBox->setEnabled(!enabled); } //----------------------------------------------------------------------------- @@ -341,20 +404,25 @@ void pqColorMapEditor::updateOpacityArraySelectorWidgets() pqInternals& internals = *this->Internals; Ui::ColorMapEditor& ui = internals.Ui; - bool enabled = internals.UseSeparateOpacityArrayAction->isEnabled(); + const bool enabled = internals.UseSeparateOpacityArrayAction->isEnabled(); ui.UseSeparateOpacityArray->setEnabled(enabled); - bool checked = internals.UseSeparateOpacityArrayAction->isChecked(); + const bool checked = internals.UseSeparateOpacityArrayAction->isChecked(); ui.UseSeparateOpacityArray->setChecked(checked); ui.OpacityLabel->setEnabled(checked && enabled); ui.DisplayOpacityWidget->setEnabled(checked && enabled); + + // when separate opacity function is enabled, disable the option to use block properties + const int currentIndex = ui.SelectedPropertiesComboBox->currentIndex(); + this->Internals->Ui.SelectedPropertiesComboBox->setCurrentIndex(enabled ? 0 : currentIndex); + this->Internals->Ui.SelectedPropertiesComboBox->setEnabled(!enabled); } //----------------------------------------------------------------------------- void pqColorMapEditor::renderViews() { - const auto source = this->Internals->ActiveRepresentation->getInput(); + const auto source = this->Internals->Representation->getInput(); // render all views which display the representation of this pqPipelineSource for (auto& view : source->getViews()) { @@ -371,32 +439,36 @@ void pqColorMapEditor::saveAsDefault() { vtkSMSettings* settings = vtkSMSettings::GetInstance(); - vtkSMProxy* proxy = this->Internals->ActiveRepresentation->getProxy(); - if (!proxy) + vtkSMProxy* repr = this->Internals->Representation->getProxy(); + if (!repr) { return; } - vtkSMProxy* lutProxy = pqSMAdaptor::getProxyProperty(proxy->GetProperty("LookupTable")); - if (lutProxy) - { - settings->SetProxySettings(lutProxy); - } - else + const std::vector luts = + this->Internals->ColorMapEditorHelper->GetSelectedLookupTables(repr); + for (vtkSMProxy* lutProxy : luts) { - qCritical() << "No LookupTable property found."; - } + if (lutProxy) + { + settings->SetProxySettings(lutProxy); + } + else + { + qCritical() << "No LookupTable property found."; + } - vtkSMProxy* scalarOpacityFunctionProxy = lutProxy - ? pqSMAdaptor::getProxyProperty(lutProxy->GetProperty("ScalarOpacityFunction")) - : nullptr; - if (scalarOpacityFunctionProxy) - { - settings->SetProxySettings(scalarOpacityFunctionProxy); - } - else - { - qCritical("No ScalarOpacityFunction property found"); + vtkSMProxy* scalarOpacityFunctionProxy = lutProxy + ? pqSMAdaptor::getProxyProperty(lutProxy->GetProperty("ScalarOpacityFunction")) + : nullptr; + if (scalarOpacityFunctionProxy) + { + settings->SetProxySettings(scalarOpacityFunctionProxy); + } + else + { + qCritical("No ScalarOpacityFunction property found"); + } } } @@ -405,75 +477,102 @@ void pqColorMapEditor::saveAsArrayDefault() { vtkSMSettings* settings = vtkSMSettings::GetInstance(); - vtkSMProxy* proxy = this->Internals->ActiveRepresentation->getProxy(); - if (!proxy) + vtkSMProxy* repr = this->Internals->Representation->getProxy(); + if (!repr) { return; } - vtkSMPropertyHelper colorArrayHelper(proxy, "ColorArrayName"); - - vtkSMProxy* lutProxy = pqSMAdaptor::getProxyProperty(proxy->GetProperty("LookupTable")); - if (lutProxy) - { - // Remove special characters from the array name - std::string sanitizedArrayName = - vtkSMCoreUtilities::SanitizeName(colorArrayHelper.GetInputArrayNameToProcess()); + auto colorMapEditorHelper = this->Internals->ColorMapEditorHelper.Get(); + // get the array names + std::vector> arrayNames = + colorMapEditorHelper->GetSelectedColorArrays(repr); + // Remove special characters from the array names + std::transform(arrayNames.begin(), arrayNames.end(), arrayNames.begin(), + [](const std::pair& pair) { + return std::make_pair(pair.first, vtkSMCoreUtilities::SanitizeName(pair.second)); + }); - std::ostringstream prefix; - prefix << ".array_" << lutProxy->GetXMLGroup() << "." << sanitizedArrayName; + const std::vector luts = colorMapEditorHelper->GetSelectedLookupTables(repr); - settings->SetProxySettings(prefix.str().c_str(), lutProxy); - } - else + for (size_t i = 0; i < arrayNames.size(); ++i) { - qCritical() << "No LookupTable property found."; - } + const std::string& arrayName = arrayNames[i].second; + vtkSMProxy* lutProxy = luts[i]; + if (lutProxy) + { + std::ostringstream prefix; + prefix << ".array_" << lutProxy->GetXMLGroup() << "." << arrayName; - vtkSMProxy* scalarOpacityFunctionProxy = lutProxy - ? pqSMAdaptor::getProxyProperty(lutProxy->GetProperty("ScalarOpacityFunction")) - : nullptr; - if (scalarOpacityFunctionProxy) - { - settings->SetProxySettings(scalarOpacityFunctionProxy); - } - else - { - qCritical("No ScalarOpacityFunction property found"); + settings->SetProxySettings(prefix.str().c_str(), lutProxy); + } + else + { + qCritical() << "No LookupTable property found."; + } + + vtkSMProxy* scalarOpacityFunctionProxy = lutProxy + ? pqSMAdaptor::getProxyProperty(lutProxy->GetProperty("ScalarOpacityFunction")) + : nullptr; + if (scalarOpacityFunctionProxy) + { + settings->SetProxySettings(scalarOpacityFunctionProxy); + } + else + { + qCritical("No ScalarOpacityFunction property found"); + } } } //----------------------------------------------------------------------------- void pqColorMapEditor::restoreDefaults() { - vtkSMProxy* proxy = this->Internals->ActiveRepresentation->getProxy(); - - vtkSMPropertyHelper colorArrayHelper(proxy, "ColorArrayName"); - std::string arrayName = - vtkSMCoreUtilities::SanitizeName(colorArrayHelper.GetInputArrayNameToProcess()); - - BEGIN_UNDO_SET(tr("Reset color map to defaults")); - if (vtkSMProxy* lutProxy = vtkSMPropertyHelper(proxy, "LookupTable").GetAsProxy()) + vtkSMProxy* repr = this->Internals->Representation->getProxy(); + + auto colorMapEditorHelper = this->Internals->ColorMapEditorHelper.Get(); + // get the array names + std::vector> arrayNames = + colorMapEditorHelper->GetSelectedColorArrays(repr); + // Remove special characters from the array names + std::transform(arrayNames.begin(), arrayNames.end(), arrayNames.begin(), + [](const std::pair& pair) { + return std::make_pair(pair.first, vtkSMCoreUtilities::SanitizeName(pair.second)); + }); + + const std::vector luts = colorMapEditorHelper->GetSelectedLookupTables(repr); + + const QString undoText = tr("Reset ") + + QCoreApplication::translate("ServerManagerXML", + colorMapEditorHelper->GetSelectedColorArrayProperty(repr)->GetXMLLabel()) + + tr(" to defaults"); + BEGIN_UNDO_SET(undoText); + for (size_t i = 0; i < arrayNames.size(); ++i) { - // Load array-specific preset, if specified. - vtkSMSettings* settings = vtkSMSettings::GetInstance(); - std::string stdPresetsKey = ".standard_presets."; - stdPresetsKey += arrayName; - if (settings->HasSetting(stdPresetsKey.c_str())) + const std::string& arrayName = arrayNames[i].second; + vtkSMProxy* lutProxy = luts[i]; + if (lutProxy) { - vtkSMTransferFunctionProxy::ApplyPreset(lutProxy, - settings->GetSettingAsString(stdPresetsKey.c_str(), 0, "").c_str(), - /*rescale=*/false); + // Load array-specific preset, if specified. + vtkSMSettings* settings = vtkSMSettings::GetInstance(); + std::string stdPresetsKey = ".standard_presets."; + stdPresetsKey += arrayName; + if (settings->HasSetting(stdPresetsKey.c_str())) + { + vtkSMTransferFunctionProxy::ApplyPreset(lutProxy, + settings->GetSettingAsString(stdPresetsKey.c_str(), 0, "").c_str(), + /*rescale=*/false); - // Should probably support setting a standard preset for opacity function at some point. */ - } - else - { - vtkSMTransferFunctionProxy::ResetPropertiesToDefaults(lutProxy, arrayName.c_str(), true); - if (vtkSMProxy* sofProxy = - vtkSMPropertyHelper(lutProxy, "ScalarOpacityFunction").GetAsProxy()) + // Should probably support setting a standard preset for opacity function at some point. */ + } + else { - vtkSMTransferFunctionProxy::ResetPropertiesToDefaults(sofProxy, arrayName.c_str(), true); + vtkSMTransferFunctionProxy::ResetPropertiesToDefaults(lutProxy, arrayName.c_str(), true); + if (vtkSMProxy* sofProxy = + vtkSMPropertyHelper(lutProxy, "ScalarOpacityFunction").GetAsProxy()) + { + vtkSMTransferFunctionProxy::ResetPropertiesToDefaults(sofProxy, arrayName.c_str(), true); + } } } } @@ -491,6 +590,25 @@ void pqColorMapEditor::setAutoUpdate(bool val) //----------------------------------------------------------------------------- void pqColorMapEditor::updateIfNeeded() { + // if an update occurred, update all other color transfer functions + if (this->Internals->ProxyWidget && this->Internals->ProxyWidget->proxy()) + { + auto firstCTF = this->Internals->ProxyWidget->proxy(); + auto changedProperties = + firstCTF->GetPropertiesWithDifferentValues(this->Internals->CopyFirstCTF); + for (vtkSMProxy* otherCTF : this->Internals->CTFs) + { + if (otherCTF && otherCTF != firstCTF) + { + for (const auto& prop : changedProperties) + { + otherCTF->GetProperty(prop.c_str())->Copy(firstCTF->GetProperty(prop.c_str())); + } + } + } + // copy the updated properties to the copy + this->Internals->CopyFirstCTF->Copy(firstCTF); + } if (this->Internals->Ui.AutoUpdate->isChecked()) { this->renderViews(); diff --git a/Qt/ApplicationComponents/pqColorMapEditor.h b/Qt/ApplicationComponents/pqColorMapEditor.h index 24a9b05d6d6..b76ce2f1aed 100644 --- a/Qt/ApplicationComponents/pqColorMapEditor.h +++ b/Qt/ApplicationComponents/pqColorMapEditor.h @@ -5,6 +5,9 @@ #define pqColorMapEditor_h #include "pqApplicationComponentsModule.h" +#include "vtkParaViewDeprecation.h" // For PARAVIEW_DEPRECATED + +#include #include class vtkSMProxy; @@ -27,11 +30,20 @@ public: pqColorMapEditor(QWidget* parent = nullptr); ~pqColorMapEditor() override; +public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) + /** + * Set the selected properties type. + */ + void setSelectedPropertiesType(int selectedPropertiesType); + protected Q_SLOTS: + ///@{ /** * slot called to update the currently showing proxies. */ - void updateActive(); + void updateActive(bool forceUpdate); + void updateActive() { this->updateActive(false); } + ///@} /** * slot called to update the visible widgets. @@ -67,8 +79,17 @@ protected Q_SLOTS: void updateIfNeeded(); protected: // NOLINT(readability-redundant-access-specifiers) - void setDataRepresentation(pqDataRepresentation* repr); - void setColorTransferFunction(vtkSMProxy* ctf); + void setRepresentation(pqDataRepresentation* repr, bool forceUpdate = false); + PARAVIEW_DEPRECATED_IN_5_13_0("Use setRepresentation instead.") + void setDataRepresentation(pqDataRepresentation* repr, bool forceUpdate = false) + { + this->setRepresentation(repr, forceUpdate); + } + void setColorTransferFunctions(std::vector ctfs); + void setColorTransferFunction(vtkSMProxy* ctf) + { + this->setColorTransferFunctions(std::vector(1, ctf)); + } protected Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) /** @@ -81,9 +102,9 @@ protected Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) private: Q_DISABLE_COPY(pqColorMapEditor) + class pqInternals; - pqInternals* Internals; - friend class pqInternals; + QScopedPointer Internals; }; #endif diff --git a/Qt/ApplicationComponents/pqEditColorMapReaction.cxx b/Qt/ApplicationComponents/pqEditColorMapReaction.cxx index decee70050d..72daf2e6991 100644 --- a/Qt/ApplicationComponents/pqEditColorMapReaction.cxx +++ b/Qt/ApplicationComponents/pqEditColorMapReaction.cxx @@ -5,6 +5,7 @@ #include "pqActiveObjects.h" #include "pqApplicationCore.h" +#include "pqColorMapEditor.h" #include "pqCoreUtilities.h" #include "pqPipelineRepresentation.h" #include "pqUndoStack.h" @@ -125,13 +126,16 @@ void pqEditColorMapReaction::editColorMap(pqPipelineRepresentation* repr) else { // Raise the color editor is present in the application. - QDockWidget* widget = + auto colorMapEditorDock = qobject_cast(pqApplicationCore::instance()->manager("COLOR_EDITOR_PANEL")); - if (widget) + if (colorMapEditorDock) { - widget->setVisible(true); - // widget->setFloating(true); - widget->raise(); + auto colorMapEditor = qobject_cast(colorMapEditorDock->widget()); + colorMapEditor->setSelectedPropertiesType( + this->ColorMapEditorHelper->GetSelectedPropertiesType()); + colorMapEditorDock->setVisible(true); + // colorMapEditorDock->setFloating(true); + colorMapEditorDock->raise(); } else { -- GitLab From 18b10e1a9be13633cb7af780ea5ecb3bdc7474fc Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 17 May 2024 19:02:36 -0400 Subject: [PATCH 29/43] paraview.simple: Support block(s) coloring --- Wrapping/Python/paraview/simple.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Wrapping/Python/paraview/simple.py b/Wrapping/Python/paraview/simple.py index bea6d95fc15..d62c1ede576 100644 --- a/Wrapping/Python/paraview/simple.py +++ b/Wrapping/Python/paraview/simple.py @@ -886,8 +886,8 @@ def ColorBy(rep=None, value=None, separate=False): # ----------------------------------------------------------------------------- -def ColorBlockBy(rep=None, selector=None, value=None, separate=False): - """Set block scalar color. This will automatically setup the color maps and others +def ColorBlocksBy(rep=None, selectors=None, value=None, separate=False): + """Set blocks scalar color. This will automatically setup the color maps and others necessary state for the representations. 'rep' must be the display properties proxy i.e. the value returned by GetDisplayProperties() function. If none is provided the display properties for the active source will be @@ -896,19 +896,21 @@ def ColorBlockBy(rep=None, selector=None, value=None, separate=False): rep = rep if rep else GetDisplayProperties() if not rep: raise ValueError("No display properties can be determined.") - if selector is None: + if selectors is None or len(selectors) == 0: raise ValueError("No selector can be determined.") - rep.SetBlockUseSeparateColorMap(selector, separate) - associationInt = rep.GetBlockColorArrayAssociation(selector) + rep.SetBlocksUseSeparateColorMap(selectors, separate) + + firstSelector = selectors[0] + associationInt = rep.GetBlockColorArrayAssociation(firstSelector) association = servermanager.GetAssociationAsString(associationInt) if associationInt != -1 else None - arrayname = rep.GetBlockColorArrayName(selector) + arrayname = rep.GetBlockColorArrayName(firstSelector) component = None if value is None: if association is not None: - rep.SetBlockScalarColoring(selector, None, servermanager.GetAssociationFromString(association)) + rep.SetBlocksScalarColoring(selectors, None, servermanager.GetAssociationFromString(association)) else: - rep.SetBlockScalarColoring(selector, None, 0) + rep.SetBlocksScalarColoring(selectors, None, 0) return if not isinstance(value, tuple) and not isinstance(value, list): value = (value,) @@ -940,11 +942,15 @@ def ColorBlockBy(rep=None, selector=None, value=None, separate=False): except ValueError: pass if component is None: - rep.SetBlockScalarColoring(selector, arrayname, servermanager.GetAssociationFromString(association)) + rep.SetBlocksScalarColoring(selectors, arrayname, servermanager.GetAssociationFromString(association)) else: - rep.SetBlockScalarColoring(selector, arrayname, servermanager.GetAssociationFromString(association), component) - rep.RescaleBlockTransferFunctionToDataRange(selector) + rep.SetBlocksScalarColoring(selectors, arrayname, servermanager.GetAssociationFromString(association), component) + rep.RescaleBlocksTransferFunctionToDataRange(selectors) + +# ----------------------------------------------------------------------------- +def ColorBlockBy(rep=None, selector=None, value=None, separate=False): + return ColorBlocksBy(rep, [selector], value, separate) # ----------------------------------------------------------------------------- def _DisableFirstRenderCameraReset(): -- GitLab From 6f25b5b8874956559738604e11d533ea08d1decd Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 17 May 2024 19:02:43 -0400 Subject: [PATCH 30/43] paraview.smstate: Support block(s) coloring --- Wrapping/Python/paraview/smstate.py | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Wrapping/Python/paraview/smstate.py b/Wrapping/Python/paraview/smstate.py index a94fa1026af..6e27b5ac0b5 100644 --- a/Wrapping/Python/paraview/smstate.py +++ b/Wrapping/Python/paraview/smstate.py @@ -323,22 +323,32 @@ def get_state(options=None, source_set=[], filter=None, raw=False, if rep.IsScalarBarVisible(view): # FIXME: this will save this multiple times, right now, # if two representations use the same LUT. + # If someone wants to fix it, also fix it below for blocks. trace.append_separated([ \ "# show color legend", "%s.SetScalarBarVisibility(%s, True)" % ( \ smtrace.Trace.get_accessor(rep), smtrace.Trace.get_accessor(view))]) - if rep.BlockLookupTableSelectors: - selectors = rep.BlockLookupTableSelectors.GetData() - for selector in selectors: - if rep.IsBlockScalarBarVisible(view, selector): - trace.append_separated([ \ - "# set color map for block %s" % selector, - "%s.SetBlockScalarBarVisibility(%s, %s, True)" % ( \ - smtrace.Trace.get_accessor(rep), - selector, - smtrace.Trace.get_accessor(view))]) + if rep.BlockColorArrayNames: + selectors = rep.BlockColorArrayNames.GetData()[::3] + isScalarVisibles = rep.IsBlocksScalarBarVisible(view, selectors) + if all(isScalarVisible == 1 for isScalarVisible in isScalarVisibles): + trace.append_separated([ \ + "# set color map for blocks", + "%s.SetBlocksScalarBarVisibility(%s, %s, True)" % ( \ + smtrace.Trace.get_accessor(rep), + smtrace.Trace.get_accessor(view), + str(selectors))]) + else: + for i in range(len(selectors)): + if isScalarVisibles[i]: + trace.append_separated([ \ + "# set color map for block %s" % selectors[i], + "%s.SetBlockScalarBarVisibility(%s, %s, True)" % ( \ + smtrace.Trace.get_accessor(rep), + smtrace.Trace.get_accessor(view), + selectors[i])]) if not rep.Visibility: traceitem = smtrace.Hide(producer, port, view) -- GitLab From 1a628dba4ed4e2c8007d1586637e02b363d022d0 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 17 May 2024 19:02:50 -0400 Subject: [PATCH 31/43] paraview.smtrace: Support block(s) coloring --- Qt/ApplicationComponents/pqColorMapEditor.cxx | 26 +++- Qt/Components/CMakeLists.txt | 1 + Qt/Components/pqBlockProxyWidget.cxx | 44 +++++++ Qt/Components/pqBlockProxyWidget.h | 32 +++++ Qt/Components/pqProxyWidget.cxx | 10 +- Qt/Components/pqProxyWidget.h | 10 +- Wrapping/Python/paraview/smtrace.py | 113 +++++++++++------- 7 files changed, 182 insertions(+), 54 deletions(-) create mode 100644 Qt/Components/pqBlockProxyWidget.cxx create mode 100644 Qt/Components/pqBlockProxyWidget.h diff --git a/Qt/ApplicationComponents/pqColorMapEditor.cxx b/Qt/ApplicationComponents/pqColorMapEditor.cxx index 3ddbb5ed898..796a961ddfb 100644 --- a/Qt/ApplicationComponents/pqColorMapEditor.cxx +++ b/Qt/ApplicationComponents/pqColorMapEditor.cxx @@ -6,6 +6,7 @@ #include "pqActiveObjects.h" #include "pqApplicationCore.h" +#include "pqBlockProxyWidget.h" #include "pqDataRepresentation.h" #include "pqEditScalarBarReaction.h" #include "pqProxyWidget.h" @@ -27,6 +28,7 @@ #include "vtkSMProxy.h" #include "vtkSMSessionProxyManager.h" #include "vtkSMSettings.h" +#include "vtkSMTrace.h" #include "vtkSMTransferFunctionProxy.h" #include "vtkSmartPointer.h" @@ -328,7 +330,19 @@ void pqColorMapEditor::setColorTransferFunctions(std::vector ctfs) return; } - pqProxyWidget* widget = new pqProxyWidget(firstCTF, this); + pqProxyWidget* widget; + if (this->Internals->ColorMapEditorHelper->GetSelectedPropertiesType() == + vtkSMColorMapEditorHelper::Representation) + { + widget = new pqProxyWidget(firstCTF, this); + } + else + { + const std::vector selectedBlockSelectors = + this->Internals->ColorMapEditorHelper->GetSelectedBlockSelectors( + this->Internals->Representation->getProxy()); + widget = new pqBlockProxyWidget(firstCTF, selectedBlockSelectors[0].c_str(), this); + } widget->setObjectName("Properties"); widget->setApplyChangesImmediately(true); widget->filterWidgets(); @@ -596,10 +610,18 @@ void pqColorMapEditor::updateIfNeeded() auto firstCTF = this->Internals->ProxyWidget->proxy(); auto changedProperties = firstCTF->GetPropertiesWithDifferentValues(this->Internals->CopyFirstCTF); - for (vtkSMProxy* otherCTF : this->Internals->CTFs) + const std::vector selectedBlockSelectors = + this->Internals->ColorMapEditorHelper->GetSelectedBlockSelectors( + this->Internals->Representation->getProxy()); + for (size_t i = 0; i < selectedBlockSelectors.size(); ++i) { + vtkSMProxy* otherCTF = this->Internals->CTFs[i]; + const std::string& selector = selectedBlockSelectors[i]; if (otherCTF && otherCTF != firstCTF) { + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", otherCTF) + .arg("selector", selector.c_str()); for (const auto& prop : changedProperties) { otherCTF->GetProperty(prop.c_str())->Copy(firstCTF->GetProperty(prop.c_str())); diff --git a/Qt/Components/CMakeLists.txt b/Qt/Components/CMakeLists.txt index 4fd0b8a4c60..7c1b00d3217 100644 --- a/Qt/Components/CMakeLists.txt +++ b/Qt/Components/CMakeLists.txt @@ -18,6 +18,7 @@ set(classes pqArraySelectionWidget pqArraySelectorPropertyWidget pqArrayStatusPropertyWidget + pqBlockProxyWidget pqCalculatorWidget pqCameraDialog pqCameraKeyFrameWidget diff --git a/Qt/Components/pqBlockProxyWidget.cxx b/Qt/Components/pqBlockProxyWidget.cxx new file mode 100644 index 00000000000..c4eb6fafe27 --- /dev/null +++ b/Qt/Components/pqBlockProxyWidget.cxx @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright (c) Kitware Inc. +// SPDX-License-Identifier: BSD-3-Clause +#include "pqBlockProxyWidget.h" + +#include "pqPropertyWidget.h" + +#include "vtkSMProxy.h" +#include "vtkSMTrace.h" + +//----------------------------------------------------------------------------------- +pqBlockProxyWidget::pqBlockProxyWidget( + vtkSMProxy* proxy, QString selector, QWidget* parent, Qt::WindowFlags flags) + : Superclass(proxy, parent, flags) + , Selector(selector) +{ +} +//----------------------------------------------------------------------------------- +pqBlockProxyWidget::~pqBlockProxyWidget() = default; + +//----------------------------------------------------------------------------------- +void pqBlockProxyWidget::apply() const +{ + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", this->proxy()) + .arg("selector", this->Selector.toUtf8().data()); + this->Superclass::applyInternal(); +} + +//----------------------------------------------------------------------------------- +void pqBlockProxyWidget::onChangeFinished() +{ + if (this->Superclass::applyChangesImmediately()) + { + pqPropertyWidget* pqSender = qobject_cast(this->sender()); + if (pqSender) + { + SM_SCOPED_TRACE(PropertiesModified) + .arg("proxy", this->proxy()) + .arg("selector", this->Selector.toUtf8().data()); + pqSender->apply(); + } + } + Q_EMIT this->changeFinished(); +} diff --git a/Qt/Components/pqBlockProxyWidget.h b/Qt/Components/pqBlockProxyWidget.h new file mode 100644 index 00000000000..efa4b25c169 --- /dev/null +++ b/Qt/Components/pqBlockProxyWidget.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright (c) Kitware Inc. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef pqBlockProxyWidget_h +#define pqBlockProxyWidget_h + +#include "pqComponentsModule.h" +#include "pqProxyWidget.h" + +#include + +class PQCOMPONENTS_EXPORT pqBlockProxyWidget : public pqProxyWidget +{ + Q_OBJECT + typedef pqProxyWidget Superclass; + +public: + pqBlockProxyWidget(vtkSMProxy* proxy, QString selector, QWidget* parent, + Qt::WindowFlags flags = Qt::WindowFlags{}); + ~pqBlockProxyWidget() override; + +public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) + void apply() const override; + +protected Q_SLOTS: + void onChangeFinished() override; + +private: + QString Selector; +}; + +#endif // pqBlockProxyWidget_h diff --git a/Qt/Components/pqProxyWidget.cxx b/Qt/Components/pqProxyWidget.cxx index ce3a69a5cbb..df8e39b765c 100644 --- a/Qt/Components/pqProxyWidget.cxx +++ b/Qt/Components/pqProxyWidget.cxx @@ -957,15 +957,21 @@ void pqProxyWidget::hideEvent(QHideEvent* hevent) } //----------------------------------------------------------------------------- -void pqProxyWidget::apply() const +void pqProxyWidget::applyInternal() const { - SM_SCOPED_TRACE(PropertiesModified).arg("proxy", this->proxy()); Q_FOREACH (const pqProxyWidgetItem* item, this->Internals->Items) { item->apply(); } } +//----------------------------------------------------------------------------- +void pqProxyWidget::apply() const +{ + SM_SCOPED_TRACE(PropertiesModified).arg("proxy", this->proxy()); + this->applyInternal(); +} + //----------------------------------------------------------------------------- void pqProxyWidget::reset() const { diff --git a/Qt/Components/pqProxyWidget.h b/Qt/Components/pqProxyWidget.h index 3b412979baa..a444c3adcc5 100644 --- a/Qt/Components/pqProxyWidget.h +++ b/Qt/Components/pqProxyWidget.h @@ -172,7 +172,7 @@ public Q_SLOTS: /** * Accepts the property widget changes changes. */ - void apply() const; + virtual void apply() const; /** * Cleans the property widget changes and resets the widgets. @@ -211,14 +211,16 @@ protected: void showEvent(QShowEvent* event) override; void hideEvent(QHideEvent* event) override; -private Q_SLOTS: + void applyInternal() const; + +protected Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) /** * Called when a pqPropertyWidget fires changeFinished() signal. * This callback fires changeFinished() signal and handles AutoUpdateVTKObjects. */ - void onChangeFinished(); + virtual void onChangeFinished(); -private: // NOLINT(readability-redundant-access-specifiers) +private: /** * create all widgets */ diff --git a/Wrapping/Python/paraview/smtrace.py b/Wrapping/Python/paraview/smtrace.py index efa9740bf29..9342d1e696a 100644 --- a/Wrapping/Python/paraview/smtrace.py +++ b/Wrapping/Python/paraview/smtrace.py @@ -145,7 +145,7 @@ class Trace(object): @classmethod def get_varname(cls, name): - """returns an unique variable name given a suggested variable name. If + """returns a unique variable name given a suggested variable name. If the suggested variable name is already taken, this method will try to find a good suffix that's available.""" name = sm._make_name_valid(name) @@ -170,10 +170,10 @@ class Trace(object): del cls.__REGISTERED_ACCESSORS[accessor.get_object()] @classmethod - def get_accessor(cls, obj): + def get_accessor(cls, obj, selector=None): """Returns an accessor for obj. If none exists, a new one may be created, if possible. Currently obj is expected to be a - :class:`servermanager.Proxy` instance. In future, we may change this to + :class:`servermanager.Proxy` instance. In the future, we may change this to be a vtkSMProxy instance instead.""" if obj is None: return None @@ -183,7 +183,7 @@ class Trace(object): except KeyError: # Create accessor if possible else raise # "untraceable" exception. - if cls.create_accessor(obj): + if cls.create_accessor(obj, selector): return cls.__REGISTERED_ACCESSORS[obj] # return "" raise Untraceable( @@ -195,7 +195,7 @@ class Trace(object): return obj in cls.__REGISTERED_ACCESSORS @classmethod - def create_accessor(cls, obj): + def create_accessor(cls, obj, selector=None): """Create a new accessor for a proxy. This returns True when a ProxyAccessor has been created, other returns False. This is needed to bring into trace proxies that were either already created when the trace @@ -249,7 +249,7 @@ class Trace(object): return True if not skip_rendering and cls.get_registered_name(obj, "lookup_tables"): pname = cls.get_registered_name(obj, "lookup_tables") - if cls._create_accessor_for_tf(obj, pname): + if cls._create_accessor_for_tf(obj, pname, selector): return True if not skip_rendering and cls.get_registered_name(obj, "piecewise_functions"): pname = cls.get_registered_name(obj, "piecewise_functions") @@ -363,39 +363,55 @@ class Trace(object): return False @classmethod - def rename_separate_tf_and_get_representation(cls, arrayName): + def rename_separate_tf_and_get_representation(cls, arrayName, selector=None): import re representation = None varname = arrayName - regex = re.compile(r"(^Separate_)([0-9]*)_(.*$)") + if selector is None: + regex = re.compile(r"(^Separate_)([0-9]*)_(.*$)") + else: + regex = re.compile(r"(^Separate_)([0-9]*)_(.*)_(.*$)") if re.match(regex, arrayName): gid = re.sub(regex, "\g<2>", arrayName) representation = next((value for key, value in simple.GetRepresentations().items() if key[1] == gid), None) if representation: repAccessor = Trace.get_accessor(representation) - arrayName = re.sub(regex, "\g<3>", arrayName) - varname = ("Separate_%s_%s" % (repAccessor, arrayName)) + if selector is None: + arrayName = re.sub(regex, "\g<3>", arrayName) + varname = ("Separate_%s_%s" % (repAccessor, arrayName)) + else: + arrayName = re.sub(regex, "\g<4>", arrayName) + varname = ("Separate_%s_%s_%s" % (repAccessor, selector, arrayName)) return arrayName, varname, representation @classmethod - def _create_accessor_for_tf(cls, proxy, regname): + def _create_accessor_for_tf(cls, proxy, regname, selector=None): import re m = re.match("^[0-9.]*(.+)\\.%s$" % proxy.GetXMLName(), regname) if m: arrayName = m.group(1) - # TODO: when block coloring properties have a GUI which calls pqProxyWidget::onChangeFinished() - # Add GetBlockColorTransferFunction tracing if proxy.GetXMLGroup() == "lookup_tables": - arrayName, varname, rep = cls.rename_separate_tf_and_get_representation(arrayName) - if rep: - repAccessor = Trace.get_accessor(rep) - args = ("'%s', %s, separate=True" % (arrayName, repAccessor)) - comment = "separate color transfer function/color map" + arrayName, varname, rep = cls.rename_separate_tf_and_get_representation(arrayName, selector) + if selector is None: + if rep: + repAccessor = Trace.get_accessor(rep) + args = ("'%s', %s, separate=True" % (arrayName, repAccessor)) + comment = "separate color transfer function/color map" + else: + args = ("'%s'" % arrayName) + comment = "color transfer function/color map" + method = "GetColorTransferFunction" + varsuffix = "LUT" else: - args = ("'%s'" % arrayName) - comment = "color transfer function/color map" - method = "GetColorTransferFunction" - varsuffix = "LUT" + if rep: + repAccessor = Trace.get_accessor(rep) + args = ("'%s', '%s', %s, separate=True" % (selector, arrayName, repAccessor)) + comment = "separate block color transfer function/block color map" + else: + args = ("'%s', '%s'" % (selector, arrayName)) + comment = "block color block transfer function/block color map" + method = "GetBlockColorTransferFunction" + varsuffix = "BlockLUT" elif proxy.GetXMLGroup() == "piecewise_functions": arrayName, varname, rep = cls.rename_separate_tf_and_get_representation(arrayName) if rep: @@ -1131,11 +1147,11 @@ class BlockTraceItems(NestableTraceItem): class PropertiesModified(NestableTraceItem): """Traces properties modified on a specific proxy.""" - def __init__(self, proxy, comment=None): + def __init__(self, proxy, selector=None, comment=None): TraceItem.__init__(self) proxy = sm._getPyProxy(proxy) - self.ProxyAccessor = Trace.get_accessor(proxy) + self.ProxyAccessor = Trace.get_accessor(proxy, selector) self.MTime = vtkTimeStamp() self.MTime.Modified() self.Comment = "#%s" % comment if not comment is None else \ @@ -1330,67 +1346,72 @@ class SetScalarColoring(RenderingMixin, TraceItem): Trace.get_accessor(self.Lut) -class SetBlockScalarColoring(RenderingMixin, TraceItem): - """Trace vtkSMPVRepresentationProxy.SetBlockScalarColoring""" +class SetBlocksScalarColoring(RenderingMixin, TraceItem): + """Trace vtkSMPVRepresentationProxy.SetBlocksScalarColoring""" - def __init__(self, display, block_selector, arrayname, attribute_type, component=None, separate=False, lut=None): + def __init__(self, display, block_selectors, arrayname, attribute_type, component=None, separate=False, luts=None): TraceItem.__init__(self) self.Display = sm._getPyProxy(display) - self.BlockSelector = block_selector + self.BlockSelectors = block_selectors self.ArrayName = arrayname self.AttributeType = attribute_type self.Component = component - self.Lut = sm._getPyProxy(lut) + # a list of luts + self.Luts = [] + if luts is not None: + for lut in luts: + self.Luts.append(sm._getPyProxy(lut)) self.Separate = separate def finalize(self): TraceItem.finalize(self) - if self.BlockSelector: + if self.BlockSelectors: if self.ArrayName: if self.Component is None: if self.Separate: Trace.Output.append_separated([ \ - "# set block scalar coloring using an separate color/opacity maps", - "ColorBlockBy(%s, %s, ('%s', '%s'), %s)" % ( \ + "# set blocks scalar coloring using an separate color/opacity maps", + "ColorBlocksBy(%s, %s, ('%s', '%s'), %s)" % ( \ str(Trace.get_accessor(self.Display)), - self.BlockSelector, + str(self.BlockSelectors), sm.GetAssociationAsString(self.AttributeType), self.ArrayName, self.Separate)]) else: Trace.Output.append_separated([ \ - "# set block scalar coloring", - "ColorBlockBy(%s, %s, ('%s', '%s'))" % ( \ + "# set blocks scalar coloring", + "ColorBlocksBy(%s, %s, ('%s', '%s'))" % ( \ str(Trace.get_accessor(self.Display)), - self.BlockSelector, + str(self.BlockSelectors), sm.GetAssociationAsString(self.AttributeType), self.ArrayName)]) else: if self.Separate: Trace.Output.append_separated([ \ - "# set block scalar coloring using an separate color/opacity maps", - "ColorBlockBy(%s, %s, ('%s', '%s', '%s'), %s)" % ( \ + "# set blocks scalar coloring using an separate color/opacity maps", + "ColorBlocksBy(%s, %s, ('%s', '%s', '%s'), %s)" % ( \ str(Trace.get_accessor(self.Display)), - self.BlockSelector, + str(self.BlockSelectors), sm.GetAssociationAsString(self.AttributeType), self.ArrayName, self.Component, self.Separate)]) else: Trace.Output.append_separated([ \ - "# set block scalar coloring", - "ColorBlockBy(%s, %s, ('%s', '%s', '%s'))" % ( \ + "# set blocks scalar coloring", + "ColorBlocksBy(%s, %s, ('%s', '%s', '%s'))" % ( \ str(Trace.get_accessor(self.Display)), - self.BlockSelector, + str(self.BlockSelectors), sm.GetAssociationAsString(self.AttributeType), self.ArrayName, self.Component)]) else: Trace.Output.append_separated([ \ - "# turn off block scalar coloring", - "ColorBlockBy(%s, %s, None)" % (str(Trace.get_accessor(self.Display)), self.BlockSelector)]) + "# turn off blocks scalar coloring", + "ColorBlocksBy(%s, %s, None)" % (str(Trace.get_accessor(self.Display)), str(self.BlockSelectors))]) # only for "Fully Trace Supplemental Proxies" support - if self.Lut: - Trace.get_accessor(self.Lut) + for i in range(len(self.Luts)): + if self.Luts[i]: + Trace.get_accessor(self.Luts[i], self.BlockSelectors[i]) class RegisterViewProxy(RenderingMixin, TraceItem): -- GitLab From 3c1f6ab5ed841d0646b2edfdff95752cc3538022 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Wed, 22 May 2024 06:28:51 -0400 Subject: [PATCH 32/43] pqBlockContextMenu: Fix block coloring --- .../pqBlockContextMenu.cxx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Qt/ApplicationComponents/pqBlockContextMenu.cxx b/Qt/ApplicationComponents/pqBlockContextMenu.cxx index 1225749f7a1..52b3d669956 100644 --- a/Qt/ApplicationComponents/pqBlockContextMenu.cxx +++ b/Qt/ApplicationComponents/pqBlockContextMenu.cxx @@ -9,8 +9,8 @@ #include "pqDoubleRangeDialog.h" #include "pqUndoStack.h" #include "vtkDataAssembly.h" -#include "vtkDataAssemblyUtilities.h" #include "vtkPVDataInformation.h" +#include "vtkSMColorMapEditorHelper.h" #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" #include "vtkSMStringVectorProperty.h" @@ -21,8 +21,7 @@ #include #include -#include -#include +#include #include #include #include @@ -189,10 +188,19 @@ bool pqBlockContextMenu::contextMenu(QMenu* menu, pqView*, const QPoint&, if (auto smProperty = reprProxy->GetProperty("BlockColors")) { menu->addAction(QIcon(":/pqWidgets/Icons/explicit_color.png"), tr("Set Block Color"), [=]() { + std::array rgb = vtkSMColorMapEditorHelper::GetColor(reprProxy); + if (!dataBlockContext.empty()) + { + auto blockRGB = vtkSMColorMapEditorHelper::GetBlockColor(reprProxy, dataBlockContext[0]); + if (vtkSMColorMapEditorHelper::IsColorValid(blockRGB)) + { + rgb = blockRGB; + } + } // it's potentially expensive to get the current color and hence I am just // using QColor here as the default color for the QColorDialog. - QColor color = QColorDialog::getColor(QColor(), pqCoreUtilities::mainWidget(), - "Set Block Color", QColorDialog::DontUseNativeDialog); + const QColor color = QColorDialog::getColor(QColor::fromRgbF(rgb[0], rgb[1], rgb[2]), + pqCoreUtilities::mainWidget(), tr("Set Block Color"), QColorDialog::DontUseNativeDialog); if (color.isValid()) { double colorF[3] = { color.redF(), color.greenF(), color.blueF() }; -- GitLab From 6282cd7d25139d5cd85a7f6f09c7697f9aaa3334 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 31 May 2024 07:21:00 -0400 Subject: [PATCH 33/43] pqMultiBlockInspectorWidget: Enable extract blocks for composites only --- Qt/Components/pqMultiBlockInspectorWidget.cxx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Qt/Components/pqMultiBlockInspectorWidget.cxx b/Qt/Components/pqMultiBlockInspectorWidget.cxx index 483a0ec60ee..bb2f5839cc6 100644 --- a/Qt/Components/pqMultiBlockInspectorWidget.cxx +++ b/Qt/Components/pqMultiBlockInspectorWidget.cxx @@ -11,11 +11,12 @@ #include "pqProxyWidget.h" #include "pqSettings.h" +#include "vtkPVDataInformation.h" #include "vtkSMPropertyGroup.h" #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" +#include "vtkSMRepresentationProxy.h" #include "vtkSMSessionProxyManager.h" -#include "vtkSMSourceProxy.h" #include "vtkSMStringVectorProperty.h" #include "vtkSmartPointer.h" @@ -67,6 +68,13 @@ class pqMultiBlockInspectorWidget::pqInternals : public QObject return false; } + static bool isCompositeDataSet(pqDataRepresentation* repr) + { + auto reprProxy = repr ? vtkSMRepresentationProxy::SafeDownCast(repr->getProxy()) : nullptr; + auto dataInfo = reprProxy ? reprProxy->GetRepresentedDataInformation() : nullptr; + return dataInfo && dataInfo->IsCompositeDataSet(); + } + public: Ui::MultiBlockInspectorWidget Ui; @@ -105,7 +113,7 @@ public: this->Representation = repr; this->update(); } - this->Ui.extractBlocks->setEnabled(repr != nullptr); + this->Ui.extractBlocks->setEnabled(pqInternals::isCompositeDataSet(repr)); } void extract() @@ -161,8 +169,11 @@ void pqMultiBlockInspectorWidget::pqInternals::update() // that have the panel visibility set to "multiblock_inspector". This way we instantiate a // pqDataAssemblyPropertyWidget which we later add to the container. this->HelperProxyWidget = new pqProxyWidget(repr->getProxy(), { "multiblock_inspector" }, {}); - QObject::connect(this->HelperProxyWidget.data(), &pqProxyWidget::changeFinished, - [repr]() { repr->renderViewEventually(); }); + QObject::connect( + this->HelperProxyWidget.data(), &pqProxyWidget::changeFinished, [this, repr]() { + this->Ui.extractBlocks->setEnabled(pqInternals::isCompositeDataSet(repr)); + repr->renderViewEventually(); + }); } else { -- GitLab From 9a8341995069d2ead13429fb98f8df048f8f8683 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 31 May 2024 08:10:03 -0400 Subject: [PATCH 34/43] Update Tests to use new MultiBlockInspector --- .../ParaView/Testing/XML/BlockOpacities.xml | 12 +++++--- .../Testing/XML/MultiBlockInspector.xml | 26 +++++++++++------ .../XML/MultiBlockInspectorAssembly.xml | 28 ++++++++++++------- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/Clients/ParaView/Testing/XML/BlockOpacities.xml b/Clients/ParaView/Testing/XML/BlockOpacities.xml index 0c092c4cb27..6321f646574 100644 --- a/Clients/ParaView/Testing/XML/BlockOpacities.xml +++ b/Clients/ParaView/Testing/XML/BlockOpacities.xml @@ -14,13 +14,17 @@ - - - + + + + + + + diff --git a/Clients/ParaView/Testing/XML/MultiBlockInspector.xml b/Clients/ParaView/Testing/XML/MultiBlockInspector.xml index 35f76640824..0fe0049c825 100644 --- a/Clients/ParaView/Testing/XML/MultiBlockInspector.xml +++ b/Clients/ParaView/Testing/XML/MultiBlockInspector.xml @@ -11,19 +11,27 @@ - - - + + + + + + - - - + + + + - - - + + + + + + + diff --git a/Clients/ParaView/Testing/XML/MultiBlockInspectorAssembly.xml b/Clients/ParaView/Testing/XML/MultiBlockInspectorAssembly.xml index e802e602256..39c2af3f552 100644 --- a/Clients/ParaView/Testing/XML/MultiBlockInspectorAssembly.xml +++ b/Clients/ParaView/Testing/XML/MultiBlockInspectorAssembly.xml @@ -11,19 +11,27 @@ - - - + + + + + + - - - + + + + - - - - + + + + + + + + -- GitLab From 63770a671012e244ecd630414e4b593165eabe83 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Wed, 5 Jun 2024 13:20:09 -0400 Subject: [PATCH 35/43] Remove second inline test from ColorOpacityEditorRangeHandles The B test is a flaky test. That is evident if you call the compare function AFTER the mouseRelease event, which is how most tests are doing it. Making this change cause the creation of different baselines value-wise, which we already have proof of since there are 2 baselines. --- .../Data/Baseline/ColorOpacityEditorRangeHandles_B.png.sha512 | 2 +- .../Baseline/ColorOpacityEditorRangeHandles_B_1.png.sha512 | 1 - .../Data/Baseline/ColorOpacityEditorRangeHandles_C.png.sha512 | 1 - .../ParaView/Testing/XML/ColorOpacityEditorRangeHandles.xml | 3 +-- Clients/ParaView/Testing/XML/test.common.cmake | 2 -- 5 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B_1.png.sha512 delete mode 100644 Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_C.png.sha512 diff --git a/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B.png.sha512 index fd9dec95216..35dca488e55 100644 --- a/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B.png.sha512 +++ b/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B.png.sha512 @@ -1 +1 @@ -6c5b2f632e784213f1cb94f610c184ae0939881b183fb1377d8ce1a547ba644904e740bb00be76ad6cf18157e592c6ccc7979d86e77c3270d5dff45763b118b8 +3718562c21f989a4fde508f6d093c95409a52949bb01b1de44a09feb1e03ac0ecfc911d45aa2b3835a100c25265e30854a9ac8fcf3938faa8b69a1fe3e354c9f diff --git a/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B_1.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B_1.png.sha512 deleted file mode 100644 index 4cbe7cc1f83..00000000000 --- a/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_B_1.png.sha512 +++ /dev/null @@ -1 +0,0 @@ -9734b8c997da32e522ee659c26c2917464ff0637b26a28d86ea67a1beec82d9f6146f6046b991d9aac149d1f948811733b86a692a8e11b4f4f314abeabd098b0 diff --git a/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_C.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_C.png.sha512 deleted file mode 100644 index 35dca488e55..00000000000 --- a/Clients/ParaView/Testing/Data/Baseline/ColorOpacityEditorRangeHandles_C.png.sha512 +++ /dev/null @@ -1 +0,0 @@ -3718562c21f989a4fde508f6d093c95409a52949bb01b1de44a09feb1e03ac0ecfc911d45aa2b3835a100c25265e30854a9ac8fcf3938faa8b69a1fe3e354c9f diff --git a/Clients/ParaView/Testing/XML/ColorOpacityEditorRangeHandles.xml b/Clients/ParaView/Testing/XML/ColorOpacityEditorRangeHandles.xml index f5fc7752a9f..19ceb955138 100644 --- a/Clients/ParaView/Testing/XML/ColorOpacityEditorRangeHandles.xml +++ b/Clients/ParaView/Testing/XML/ColorOpacityEditorRangeHandles.xml @@ -9,7 +9,6 @@ - @@ -27,5 +26,5 @@ - + diff --git a/Clients/ParaView/Testing/XML/test.common.cmake b/Clients/ParaView/Testing/XML/test.common.cmake index aeb004572b4..69b40246cf5 100644 --- a/Clients/ParaView/Testing/XML/test.common.cmake +++ b/Clients/ParaView/Testing/XML/test.common.cmake @@ -317,8 +317,6 @@ ExternalData_Expand_Arguments(ParaViewData _ "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityEditorRangeHandles.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityEditorRangeHandles_A.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityEditorRangeHandles_B.png}" - "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityEditorRangeHandles_B_1.png}" - "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityEditorRangeHandles_C.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityTableEditorHistogram_A.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityTableEditorHistogram_B.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ColorOpacityTableEditorHistogram_C.png}" -- GitLab From 95b20c64c0c0759095409a46579aff1446fcd39d Mon Sep 17 00:00:00 2001 From: Cory Quammen Date: Wed, 5 Jun 2024 20:05:58 -0400 Subject: [PATCH 36/43] Undock MultiBlock Inspector in tests The MultiBlock Inspector starts out docked on the right side of the main window. This squeezed the view on macOS to the point of changing the zoom factor in the generated test images, causing test failures. This is solved by undocking the MultiBlock Inspector as soon as it is shown. --- .../Testing/Data/Baseline/CONVERGECFDReaderMesh_1.png.sha512 | 1 + Clients/ParaView/Testing/XML/BlockContextMenu.xml | 3 +++ Clients/ParaView/Testing/XML/CONVERGECFDReader.xml | 3 ++- Clients/ParaView/Testing/XML/SpreadSheet3.xml | 3 +++ Clients/ParaView/Testing/XML/ZoomToData.xml | 3 +++ Clients/ParaView/Testing/XML/test.common.cmake | 1 + 6 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 Clients/ParaView/Testing/Data/Baseline/CONVERGECFDReaderMesh_1.png.sha512 diff --git a/Clients/ParaView/Testing/Data/Baseline/CONVERGECFDReaderMesh_1.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/CONVERGECFDReaderMesh_1.png.sha512 new file mode 100644 index 00000000000..0c183bf1134 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/CONVERGECFDReaderMesh_1.png.sha512 @@ -0,0 +1 @@ +ca12e20671dfa7f7fd264ff311601f41401ae79c9b1b831301df3864afeedec24666e36ee317a83c36b3c4a4a7e97431f86fb55fbf1b9636e1e93abc91983d08 diff --git a/Clients/ParaView/Testing/XML/BlockContextMenu.xml b/Clients/ParaView/Testing/XML/BlockContextMenu.xml index 211e64b89df..b726256e32c 100644 --- a/Clients/ParaView/Testing/XML/BlockContextMenu.xml +++ b/Clients/ParaView/Testing/XML/BlockContextMenu.xml @@ -4,6 +4,9 @@ + + + diff --git a/Clients/ParaView/Testing/XML/CONVERGECFDReader.xml b/Clients/ParaView/Testing/XML/CONVERGECFDReader.xml index 0de377a876a..4bf2e2aa188 100644 --- a/Clients/ParaView/Testing/XML/CONVERGECFDReader.xml +++ b/Clients/ParaView/Testing/XML/CONVERGECFDReader.xml @@ -9,7 +9,8 @@ - + + / diff --git a/Clients/ParaView/Testing/XML/SpreadSheet3.xml b/Clients/ParaView/Testing/XML/SpreadSheet3.xml index 43bf2ea5b96..25e7b247b45 100644 --- a/Clients/ParaView/Testing/XML/SpreadSheet3.xml +++ b/Clients/ParaView/Testing/XML/SpreadSheet3.xml @@ -11,6 +11,9 @@ + + + diff --git a/Clients/ParaView/Testing/XML/ZoomToData.xml b/Clients/ParaView/Testing/XML/ZoomToData.xml index be18db73147..95a10002e47 100644 --- a/Clients/ParaView/Testing/XML/ZoomToData.xml +++ b/Clients/ParaView/Testing/XML/ZoomToData.xml @@ -2,6 +2,9 @@ + + + diff --git a/Clients/ParaView/Testing/XML/test.common.cmake b/Clients/ParaView/Testing/XML/test.common.cmake index 69b40246cf5..2889064636c 100644 --- a/Clients/ParaView/Testing/XML/test.common.cmake +++ b/Clients/ParaView/Testing/XML/test.common.cmake @@ -346,6 +346,7 @@ ExternalData_Expand_Arguments(ParaViewData _ "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ContourCellData.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/CONVERGECFDCGNSReader.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/CONVERGECFDReaderMesh.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/CONVERGECFDReaderMesh_1.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/CONVERGECFDReaderParcels.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/ConvertToMoleculeBonds.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/CriticalTimeBetweenX.png}" -- GitLab From 9fd4938b9fca5d65dc285bf77504eb2a5cec02d8 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 6 Jun 2024 10:08:41 -0400 Subject: [PATCH 37/43] pqMultiBlockInspectorWidget: Show state legend --- .../UI/pqMultiBlockInspectorWidget.ui | 364 +++++++++++++++++- Qt/Components/pqMultiBlockInspectorWidget.cxx | 20 + 2 files changed, 368 insertions(+), 16 deletions(-) diff --git a/Qt/Components/Resources/UI/pqMultiBlockInspectorWidget.ui b/Qt/Components/Resources/UI/pqMultiBlockInspectorWidget.ui index 247721b3588..fd2f5860eca 100644 --- a/Qt/Components/Resources/UI/pqMultiBlockInspectorWidget.ui +++ b/Qt/Components/Resources/UI/pqMultiBlockInspectorWidget.ui @@ -8,14 +8,14 @@ 0 0 - 480 + 662 640 MultiBlock Inspector - + 2 @@ -82,18 +82,264 @@ - + + + + 75 + true + + - <html><head/><body><p><span style=" font-weight:600;">Color Legend:</span><br/><img src=":/pqWidgets/Icons/no_color.png"/> - using coloring parameters from display<br/><img src=":/pqWidgets/Icons/inherited_color.png"/> - color is inherited from a parent node or color map<br/><img src=":/pqWidgets/Icons/explicit_color.png"/> - color explicitly specified for this node</p><p><span style=" font-weight:600;">Opacity Legend:</span><br/><img src=":/pqWidgets/Icons/no_opacity.png"/> - using opacity parameters from display<br/><img src=":/pqWidgets/Icons/inherited_opacity.png"/> - opacity is inherited from a parent node<br/><img src=":/pqWidgets/Icons/explicit_opacity.png"/> - opacity explicitly specified for this node</p></body></html> + State Legend: - Qt::RichText + Qt::PlainText true + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateDisabled.svg + + + + + + + Property/ies is/are disabled because it/they cannot be edited. + + + Qt::PlainText + + + true + + + + + + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateRepresentationInherited.svg + + + + + + + Property/ies are inherited from the representation. + + + Qt::PlainText + + + true + + + + + + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateBlockInherited.svg + + + + + + + Property/ies is/are inherited from block(s). + + + Qt::PlainText + + + true + + + + + + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateMixedInherited.svg + + + + + + + Property/ies is/are inherited from block(s) and the representation. + + + Qt::PlainText + + + true + + + + + + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateSet.svg + + + + + + + Property/ies is/are set in block(s). + + + Qt::PlainText + + + true + + + + + + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateSetAndRepresentationInherited.svg + + + + + + + Property/ies is/are set in block(s) and inherited from the representation. + + + Qt::PlainText + + + true + + + + + + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateSetAndBlockInherited.svg + + + + + + + Property/ies is/are set in block(s) and inherited from block(s). + + + Qt::PlainText + + + true + + + + + + + + + + + + 0 + 0 + + + + :/pqWidgets/Icons/pqStateSetAndMixedInherited.svg + + + + + + + Property/ies is/are set in block(s) and inherited from block(s) and the representation. + + + Qt::PlainText + + + true + + + + + @@ -103,18 +349,104 @@ showHints toggled(bool) - label + labelHeader + setVisible(bool) + + + showHints + toggled(bool) + iconStateDisabled + setVisible(bool) + + + showHints + toggled(bool) + labelStateDisabled + setVisible(bool) + + + showHints + toggled(bool) + iconStateRepresentationInherited + setVisible(bool) + + + showHints + toggled(bool) + labelStateRepresentationInherited + setVisible(bool) + + + showHints + toggled(bool) + iconStateBlockInherited + setVisible(bool) + + + showHints + toggled(bool) + labelStateBlockInherited + setVisible(bool) + + + showHints + toggled(bool) + iconStateMixedInherited + setVisible(bool) + + + showHints + toggled(bool) + labelStateMixedInherited + setVisible(bool) + + + showHints + toggled(bool) + iconStateSet + setVisible(bool) + + + showHints + toggled(bool) + labelStateSet + setVisible(bool) + + + showHints + toggled(bool) + iconStateSetAndRepresentationInherited + setVisible(bool) + + + showHints + toggled(bool) + labelStateSetAndRepresentationInherited + setVisible(bool) + + + showHints + toggled(bool) + iconStateSetAndBlockInherited + setVisible(bool) + + + showHints + toggled(bool) + labelStateSetAndBlockInherited + setVisible(bool) + + + showHints + toggled(bool) + iconStateSetAndMixedInherited + setVisible(bool) + + + showHints + toggled(bool) + labelStateSetAndMixedInherited setVisible(bool) - - - 440 - 431 - - - 239 - 541 - - diff --git a/Qt/Components/pqMultiBlockInspectorWidget.cxx b/Qt/Components/pqMultiBlockInspectorWidget.cxx index bb2f5839cc6..e80f3383506 100644 --- a/Qt/Components/pqMultiBlockInspectorWidget.cxx +++ b/Qt/Components/pqMultiBlockInspectorWidget.cxx @@ -21,6 +21,9 @@ #include "vtkSmartPointer.h" #include +#include + +#include //============================================================================= class pqMultiBlockInspectorWidget::pqInternals : public QObject @@ -78,6 +81,13 @@ class pqMultiBlockInspectorWidget::pqInternals : public QObject public: Ui::MultiBlockInspectorWidget Ui; + static void resizeLabelPixmap(QLabel* iconLabel, int iconSize) + { + const QPixmap pixmap = iconLabel->pixmap(Qt::ReturnByValue); + iconLabel->setPixmap( + pixmap.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + } + pqInternals(pqMultiBlockInspectorWidget* self) { this->Ui.setupUi(self); @@ -86,7 +96,17 @@ public: const bool checked = settings->value("pqMultiBlockInspectorWidget/ShowHints", true).toBool(); this->Ui.showHints->setChecked(checked); } + const int iconSize = std::max(self->style()->pixelMetric(QStyle::PM_SmallIconSize), 20); + pqInternals::resizeLabelPixmap(this->Ui.iconStateDisabled, iconSize); + pqInternals::resizeLabelPixmap(this->Ui.iconStateRepresentationInherited, iconSize); + pqInternals::resizeLabelPixmap(this->Ui.iconStateBlockInherited, iconSize); + pqInternals::resizeLabelPixmap(this->Ui.iconStateMixedInherited, iconSize); + pqInternals::resizeLabelPixmap(this->Ui.iconStateSet, iconSize); + pqInternals::resizeLabelPixmap(this->Ui.iconStateSetAndRepresentationInherited, iconSize); + pqInternals::resizeLabelPixmap(this->Ui.iconStateSetAndBlockInherited, iconSize); + pqInternals::resizeLabelPixmap(this->Ui.iconStateSetAndMixedInherited, iconSize); } + ~pqInternals() override { if (auto settings = pqApplicationCore::instance()->settings()) -- GitLab From 875c2ec243302971b675401dc90c3ccb16d209ca Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Thu, 6 Jun 2024 12:59:52 -0400 Subject: [PATCH 38/43] Add MultiBlockInspectorProperties.xml Test --- .../MultiBlockInspectorProperties.png.sha512 | 1 + ...MultiBlockInspectorProperties_A.png.sha512 | 1 + ...MultiBlockInspectorProperties_B.png.sha512 | 1 + ...MultiBlockInspectorProperties_C.png.sha512 | 1 + ...MultiBlockInspectorProperties_D.png.sha512 | 1 + ...MultiBlockInspectorProperties_E.png.sha512 | 1 + ...MultiBlockInspectorProperties_F.png.sha512 | 1 + ...MultiBlockInspectorProperties_G.png.sha512 | 1 + ...MultiBlockInspectorProperties_H.png.sha512 | 1 + ...MultiBlockInspectorProperties_I.png.sha512 | 1 + ...MultiBlockInspectorProperties_J.png.sha512 | 1 + ...MultiBlockInspectorProperties_K.png.sha512 | 1 + .../XML/MultiBlockInspectorProperties.xml | 94 +++++++++++++++++++ .../ParaView/Testing/XML/test.common.cmake | 13 +++ 14 files changed, 119 insertions(+) create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_A.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_B.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_C.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_D.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_E.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_F.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_G.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_H.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_I.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_J.png.sha512 create mode 100644 Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_K.png.sha512 create mode 100644 Clients/ParaView/Testing/XML/MultiBlockInspectorProperties.xml diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties.png.sha512 new file mode 100644 index 00000000000..41600077917 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties.png.sha512 @@ -0,0 +1 @@ +380ebc90ee3e1beefb00b76271013e493946c2835d375eaa20a06a92ac78a6d35b9f166b1a09a635bfdcc4c3d7118431ce47be41ba0170485e666524de00352d diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_A.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_A.png.sha512 new file mode 100644 index 00000000000..28c4bf71062 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_A.png.sha512 @@ -0,0 +1 @@ +a4595312c8c99690258a4b85fa27b638ee79f80a6072d8bc98fc42a84df89dd60848dc262173ce6d2ab624ec50502af3b80cac3f77ba04778443e10b6cb06703 diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_B.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_B.png.sha512 new file mode 100644 index 00000000000..2608c50e781 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_B.png.sha512 @@ -0,0 +1 @@ +120d703db9dc50cbac695e2cef1e60c5cfd999ff10d55f665f0d03c704e5051939541ffeed0f20c2c0abc0e2adfd82dc62238e031c9fc8b31aa48c20551796c6 diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_C.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_C.png.sha512 new file mode 100644 index 00000000000..2caeee664cb --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_C.png.sha512 @@ -0,0 +1 @@ +083f2ae2acdf20b73334760fabd9b45cd379e0c5f919f210bf94091fbe2430f1f40a12e5e32fee415411809a9666be08bbb413f63a02b3d6276f799c9607dd55 diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_D.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_D.png.sha512 new file mode 100644 index 00000000000..f59c71ff442 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_D.png.sha512 @@ -0,0 +1 @@ +672c9b81f807cef86fc21228952b53ad2ced7e004e3286838fd201ba1a5cbc21181ec930c3685b50eba8feb0b28cd9db712c5a37c4af3ab788a999f58c150411 diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_E.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_E.png.sha512 new file mode 100644 index 00000000000..2608c50e781 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_E.png.sha512 @@ -0,0 +1 @@ +120d703db9dc50cbac695e2cef1e60c5cfd999ff10d55f665f0d03c704e5051939541ffeed0f20c2c0abc0e2adfd82dc62238e031c9fc8b31aa48c20551796c6 diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_F.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_F.png.sha512 new file mode 100644 index 00000000000..41600077917 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_F.png.sha512 @@ -0,0 +1 @@ +380ebc90ee3e1beefb00b76271013e493946c2835d375eaa20a06a92ac78a6d35b9f166b1a09a635bfdcc4c3d7118431ce47be41ba0170485e666524de00352d diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_G.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_G.png.sha512 new file mode 100644 index 00000000000..53987c813b7 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_G.png.sha512 @@ -0,0 +1 @@ +3cbc7803b841e8ed8d15502d4d1dcdd61de15075efb6ba1f05a04f1de5070b9d79b90758efcc905887cd9607610c77c5f91a30eee89c115b937926124e1f1819 diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_H.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_H.png.sha512 new file mode 100644 index 00000000000..41600077917 --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_H.png.sha512 @@ -0,0 +1 @@ +380ebc90ee3e1beefb00b76271013e493946c2835d375eaa20a06a92ac78a6d35b9f166b1a09a635bfdcc4c3d7118431ce47be41ba0170485e666524de00352d diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_I.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_I.png.sha512 new file mode 100644 index 00000000000..77a526e970f --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_I.png.sha512 @@ -0,0 +1 @@ +0b655fad4e9dee05831bd4c342f05cf693c048624da7e68e90beeaf83ae193f5403c46e5ddc59482c03e0bfb953f76e1767e2ca778e1e80862df6f9513e0ce2e diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_J.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_J.png.sha512 new file mode 100644 index 00000000000..56cfcd9d9ae --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_J.png.sha512 @@ -0,0 +1 @@ +b34f695ea7c0803bfdd5485beb17a79bf6fec2938992b1d5ccf78b6171f20a513161f574654fe4edee08c0510716ab36d0935933ccffb0cc201e1e9b03c21dad diff --git a/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_K.png.sha512 b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_K.png.sha512 new file mode 100644 index 00000000000..77a526e970f --- /dev/null +++ b/Clients/ParaView/Testing/Data/Baseline/MultiBlockInspectorProperties_K.png.sha512 @@ -0,0 +1 @@ +0b655fad4e9dee05831bd4c342f05cf693c048624da7e68e90beeaf83ae193f5403c46e5ddc59482c03e0bfb953f76e1767e2ca778e1e80862df6f9513e0ce2e diff --git a/Clients/ParaView/Testing/XML/MultiBlockInspectorProperties.xml b/Clients/ParaView/Testing/XML/MultiBlockInspectorProperties.xml new file mode 100644 index 00000000000..7b34dcde52a --- /dev/null +++ b/Clients/ParaView/Testing/XML/MultiBlockInspectorProperties.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/ParaView/Testing/XML/test.common.cmake b/Clients/ParaView/Testing/XML/test.common.cmake index 2889064636c..45e7f624b66 100644 --- a/Clients/ParaView/Testing/XML/test.common.cmake +++ b/Clients/ParaView/Testing/XML/test.common.cmake @@ -529,6 +529,18 @@ ExternalData_Expand_Arguments(ParaViewData _ "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/Molecule-Liquorice.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockChartSelectionPlotView.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockChartSelectionRenderView.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_A.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_B.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_C.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_D.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_E.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_F.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_G.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_H.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_I.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_J.png}" + "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorProperties_K.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorSelection-SelectBlock2.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorSelection-SelectElementBlocks.png}" "DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/MultiBlockInspectorSelection-HideBlock1.png}" @@ -1025,6 +1037,7 @@ list (APPEND TESTS_WITH_INLINE_COMPARES LogoSourcesInChartViews.xml MemoryInspectorPanel.xml Molecule.xml + MultiBlockInspectorProperties.xml MultiBlockInspectorSelection.xml MultiBlockChartSelection.xml MultipleColorOnSelection.xml -- GitLab From f4ba717ef4fa914adbbf57ef5e3f63d033f10500 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 7 Jun 2024 10:19:22 -0400 Subject: [PATCH 39/43] pqApplyBehavior: Support block(s) coloring in showData --- Qt/ApplicationComponents/pqApplyBehavior.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Qt/ApplicationComponents/pqApplyBehavior.cxx b/Qt/ApplicationComponents/pqApplyBehavior.cxx index e9a40be091b..0e4649299d8 100644 --- a/Qt/ApplicationComponents/pqApplyBehavior.cxx +++ b/Qt/ApplicationComponents/pqApplyBehavior.cxx @@ -391,6 +391,11 @@ void pqApplyBehavior::showData(pqPipelineSource* source, pqView* view) { vtkSMColorMapEditorHelper::SetScalarBarVisibility(reprProxy, preferredView, true); } + if (vtkSMColorMapEditorHelper::GetAnyBlockUsingScalarColoring(reprProxy)) + { + vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility(reprProxy, preferredView, + vtkSMColorMapEditorHelper::GetColorArraysBlockSelectors(reprProxy), true); + } } // Save the newly created representation for further fine-tuning in -- GitLab From 22106cf134b28cb136709c257caa8d4b3c51ca39 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 7 Jun 2024 10:21:19 -0400 Subject: [PATCH 40/43] pqDeleteReaction: Support block(s) coloring in aboutToDelete --- Qt/ApplicationComponents/pqDeleteReaction.cxx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Qt/ApplicationComponents/pqDeleteReaction.cxx b/Qt/ApplicationComponents/pqDeleteReaction.cxx index e287c0a75ba..807779f6105 100644 --- a/Qt/ApplicationComponents/pqDeleteReaction.cxx +++ b/Qt/ApplicationComponents/pqDeleteReaction.cxx @@ -407,10 +407,17 @@ void pqDeleteReaction::aboutToDelete(pqProxy* source) // preference is such. if (reprProxy && vtkPVGeneralSettings::GetInstance()->GetScalarBarMode() == - vtkPVGeneralSettings::AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS && - vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)) + vtkPVGeneralSettings::AUTOMATICALLY_SHOW_AND_HIDE_SCALAR_BARS) { - vtkSMColorMapEditorHelper::SetScalarBarVisibility(reprProxy, viewProxy, true); + if (vtkSMColorMapEditorHelper::GetUsingScalarColoring(reprProxy)) + { + vtkSMColorMapEditorHelper::SetScalarBarVisibility(reprProxy, viewProxy, true); + } + if (vtkSMColorMapEditorHelper::GetAnyBlockUsingScalarColoring(reprProxy)) + { + vtkSMColorMapEditorHelper::SetBlocksScalarBarVisibility(reprProxy, viewProxy, + vtkSMColorMapEditorHelper::GetColorArraysBlockSelectors(reprProxy), true); + } } view->render(); } -- GitLab From 243d1017ec00294bdd2adcf5560c220e07496c99 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Fri, 7 Jun 2024 10:40:38 -0400 Subject: [PATCH 41/43] Add changelog --- .../release/dev/MultiBlockInspectorNew.png | Bin 0 -> 124595 bytes .../release/dev/MultiBlockInspectorOld.png | Bin 0 -> 59399 bytes ...ector-refactoring-color-blocks-by-array.md | 29 ++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 Documentation/release/dev/MultiBlockInspectorNew.png create mode 100644 Documentation/release/dev/MultiBlockInspectorOld.png create mode 100644 Documentation/release/dev/multiblock-inspector-refactoring-color-blocks-by-array.md diff --git a/Documentation/release/dev/MultiBlockInspectorNew.png b/Documentation/release/dev/MultiBlockInspectorNew.png new file mode 100644 index 0000000000000000000000000000000000000000..2254b845503ec15df971563af03ad845f8f41a17 GIT binary patch literal 124595 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� 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%0A3f{-BX+QFvZU@bB5QUDtKO zyef`e&M`1EtokPy^!Kl0&4;%PY^<#t*9OZJ%9kmeS$pYoN9U0nEXngey4?MA-{|&s zxpnXK&cEe3Dqp{N-s!x{x8$`&zi;WQ%&N$|(-M_se`(A1wfB|!6enud+*&HQf4}tU z_PdWe@>2Q(EuU7E#$5REHez1ko6wTVXEIka9G*4oUJY3-9PrZK@?^}D`A+b)wk2)3 zh0oWm$m`UQOSq9@qIvtbc-aU9FQ_p?4?e_02 z-!CqZb99(6L(sHVI=MNX@9WFZLKk`ESNy)uDpuF7iQhU!wOM)Q_5y>~la33dghh3n zS#qUn)o%L2eEm9cMTph=s~VtDc21$rUFR-3>P>Cn3W(mjVMAxU^sIE1 zjSn{mx?G8Jux@u+JaI-s5{uqxV;HK-~3j!p6qpM z#q&+7uTN!N-nF3iOt60W$HL&txf!{)4>a#s-!D{VG4bkYG2#F3oSq$9m+<dYD>}x_E9b79SWwWrS_rATJVQS=^VdXUOwTJaj_UiTb8wRj=|- zU$Pzrdml4@zOKpJL~hrxadstyV)-TKM1NYPUV`qV0KKE*u5)8l_w@Pr=R5f9xi?Ma&hMIJ5S%PxU^52 zIXQQOr=+*CF{j7shffmDY`$Ip$>D)()y;su`ZEQ87C+|b=T5mWsW;}8fsDRn%Kd$= zZCB)$3niW^x>a8I<@CCA=j7y+lusWfGN{Vy#C^Pd;h zZ$*E;zLChK!1K-@K4CfM$hPXZFFDUwT5b*6Y5BLh>SCIs{Vpl~Sce-We+)cD8{?PC(_II>iisEn>NoQMM|qgc#Di;vacRU z2<775&$A?df$p)c?mHLDxZ18v`^Id&alvPqM^&6WuP<@9KbCnHx2xB6b)L!8{f#xP z+w88G&RP+8{?@JSid7lqV#m)c@-Ampe$-T8a_ZTgqwS)fr7jGfc>rz0;3adu#macz z{F)PRTik!y#+pyz7UczMzpKQ)y}cC_eS53ciDkScQlGu#%}?Ln_U2mNwKr#%wry8l zcF5N6@2tL4H`t4nJNt^J_F-8p&HiW0i%9jkFFu`l`Jv?b+!;x34c{eR5O#rctxwi_MGOWyZ%Q zt8FxSsC+Hu$)fy+P4UlHmSn#74egb`wQP@Zm0s;Xsl%7{8J;oSba?x7rdw^+!JL|U zbJ;(=+Y@0H@pWOaN1()eN%qNg4;sts@0IGmy=Zo*{)(H~O#g&avpm22QxaY}Q8bVD z*8^Gc)(N$2)@(A6$xUuWl6)@(+79*D*1qF3!0t zcR9a6lwA!XS6WNXo}^FOBj*b!+Xq@}oC^4Nw4 z>rAFj*Ggjztdf6aw<+t|=561!+56K!yG5({MW21K<7SH1mz7H8iVHgVuZcc*{wnML zQ+F@Z_X141<;L6HdH9Q$h7^8Ue6!>IkD3Gt*85CGcdYBZ-rLUKq*Z5n_ORN;fY~t; zf%Q+=%);g#crtbV<>Y_L2E_~hHKcCI&G|WrrD|R54D-(nzE!n{R+LL^STxu4(}`<8 z`sW^<*6g2g@y-nFtC2-cSe~0E`+bGz^`Nu!s-A9rKV5V6ywrELGcDiA$e;h)<9DBX zMtFZx$>M9b=I-g*l$o0xH*wC=-q}xcZmayYxh+!1Xgeo+ZmQsE$xjElzO2dmc<_tZmJ5s0SN2(5{B`DK!v~wK+UqupR@SP_oH;Z9 zblCfRxvQmOTX|CQ&gNU#>o=JfIfVtTWygq>Gb7OQ965L=M(n&sk6VbF?HkW9Kpu!{fe z;&7LX?~&#?&MzpA3kYXlZ{eD1y0jEDz2gD`|Kv`Y{9LWWcklHTsD44gO4|~z>$7Ih z&Mvw3?u-AUkK4K#Lh|0Wz7>D#HARqxvC~CqSuV_gdjeLvdoDbA5?eHD)wY`6*^9m6 zJW4)+Oa|e55>c18rj~v%`K!j!WMK}UxZn+3$9wbL7ynCfX%0n|v&1y};ESj4?@C@< ztgkZ3pqlylsiMB;^%76d#-5Sh-%%?$i|Mh~I`#gg*E_z(tQSaKwPRx@i|ntOlk%r7 zozmQ~EmN(VZ`tCFGam8TpPldf>1>d+5XehErw8h(ug~C%Tba5oPQ(B5f5lBdKR?O) zHm5q^ow8G?xtWn_o#k^8HW?YwhUfmT5TeO_z!JBWYac zbNIE;-|t)GgxxW)3HJ_`1|6$W4Z+`7g! zsPO;x`8WQ*ej9W<7-^=#bH=2Yb+SL2{M*((eRlhssUm3QdQjfIb&Ey4h+P2Pw_r*a z<^pKrSboXtg3jFPHa2%A&Yt$r;LUP&k3Sz3OK)#KxV7o&%8E}GR=c_7&)slO)8y+p za)c|qoIhqw?|YGXJ>bb<3GwSUF>(3Tpnz?Ryt;>h$aTJw&g2u3z?k zsoC0@JJ)`-*L7=Mv}^K-E?uuEy_Ze1#nNNWFP8p$f9Cn)r-LO#?@7M?-?LmBG&}l# zdePp+x#HWe$GM#i(|YQb_!+(u#?p14@trZaWudjdc><7;u zHuO%FNx#2OC5@w@$=d$x!ty2hdPSCgrAjXhYsI&J;<_zrbi>rHt>FJGi<4_NOxpcO z`lI6H?aTIwsETaz+gx>{nx)!0IXk?$&G4g2oSxRgN8h%ce|h7~{8@cPYjR{(-hJr3 zPK!|_X3kX+?c(AyXV+$$eYe@&R+SW6&vX29{FeD!H)`{J`nJZadSZESoR8YiCeyWx zr4w74f*&(aFrRba<)SsKjOIFX|GvCyp?Y7D`yQv{nY&(_vD$OnSZ!@$Jo~p`YP53s zI@iT{+B=u1Et#=BUEtj((8A%DVg4tQ{2$59V|BZHDvrxZ%-X&6ar9ljY;DVR`_8`H z|3@XVb^D@7^U6DSEPLO*|M56c|M*e$N%y$>CM-0&6}Bs8>Febm?D+ea_?Z@;IeT{E z{{7Q_uU}>%zjpcN7{BSftP^#P{+#+HOR{BsyK<4X_7m80XYbR!_Y?{vivPqsxnWTu zbf=Q(_PXa;di+n~ZXa8+&UeLAjpNtW$^|lu6dMixVSRIF^PhEl zzif%b`5&cu+A(*d48LDbM;@wAoGJ6~N1a8k^p520+~(x7Y|McRg9#g|3e?F}4*)4wb#!cxJ*>k)eEeYg#k=1bd-}#T` zA%)+MlzsdA^56+oPL^5PJJy#Ra{MMR;o-Gf*6gb5E^REy+2%U@?Fql$GJcB>ymY{u z?MczE51b-#doyxEMeG8R2D~du_r>U?rR{p45`>^JcugeawB` z@|kzl=gq(4zOlmFV%wd^uWY?@w(k7WH96`v=gi%ncV>G({;13SKhLXo8uLuC)c&c3 zd@D6COGw_Iowd(>rbxm9NAnLE|1<7&-)?&BE*xojbfUWZz6YC6Zsgf$b>zD2Q~mnd zDQeTh{)(6^jQ^gzFZzdF5vPam+ZC%+gO++(pY>P&lxdhPD!=2^$0w)Ep7r#@hr8EJ zn$UY`ovCtl?;)+{HuulZ@#~+lJm-e_+zY3&y3%vfBW}O@lOcTkMe5_ib3do%FY{A- z_E%@yn;$*D+&+AH!E&oSqH)#Jg0w&D<*tgnTq1dO!6)xe&x_yQW-OWcwc-2qoWzdx zoD&YOKYo6}Dp`Nq$4x<}v`CdO{= zp?kkhP6H>GD2?!4+d8Do-3!wlK7Do-n|iw*S((RlZ+30rih*8Mf!FyMfc*28#4R;tMTgRgFL!_cWn7_gdeO&ppMAgW{6DR* zHvWEQj%NC~Ev);$C8u`FY&#e2*mV86OtcNha;3Ff4}1`xqH>gP>Gcn%S={ncy)s&B z_O6hdTw%^^D=po_{b^0rrwJlc6&)KDS$<4$ZZfZ1pB|prJ=Lc7$dTn7Gyax;Q|kgP z&9Yw_;vBcoe7%LvzI?x>M<#}N$*jC~Ao%&w(|4}5dwu(MySCsH=R)rkrgzNKHh=Su@7l`AShT+IUy)wf*5){C8HJ;(%4D@xt}C$9+y0{> zsQ6V|rSi_gd8f~Z+UzQ7Ww-ozW@UP>(?W-Z0SU7ECd{_v-|_#!-Tn}dQ`eOj&0`EN zd!rJ_y6VY&i^9`09t5nPUU_n_fOF0wUEf*ZdS>;NoYOnK&p$h&VXwoT5_DsGa;=vf z|G%mZFZGUj&P*P07iLJEWiwX)#dI=jQ#6b3#}@ZDU3Oh6FN&o^s$@eo=X6QAbl=*y z!{SEZ#qIVR{XQt4^Utbs z=XHx}L-ySVZrxZtvv1|QwMh>9drws9CC0U!T^%`l!ohBz3zx6gX0FNnS(a8Xlf z-thGIJ#ee1bEdA(k*3fm{Q@x- z#w|<|J8SiNPmA(~UkhTtGIwt1>|AH3DDy)roOz=2zb<^dvo^+H&FO}$U99`li`CMk zJ^R*XJY4H$+ti?7s8aKb?--j{spiSkHy5<(g|_EB^jX}T6SH%MJX@<_{X>Oo`I64M zhbR3p{-t^>*!IuV8IO&cln)!|X&isj+ni~wH}8RAaHQ~?2FGWfX{A!0PMqQbCMN)bMMkxtDSC%%LDC~Zp`y;kMZVf;##=ndJo5{>Hfb&<6pJf<&^G! za(sDS9 zCMFJ!6>dwpUg>QAbmRArM~|QOZO+Z)v2i%f8}DyfFK2&jzW!SS(eM4TPuq?#$CsPh zIVkoTt3L|3x$|+B!r?QAnT@ZOKAPlHP*is8=hJ#WjZ;%X#HwDrJhXZF%lx~$!_-z7 z=jJ^AQTbQ!+dG@qy1mP;T+;tx_}V;aoBe^Ek;OJwZd4Sx``I$0KXC5kzW2^ZE@mg|H`k}-+f0}JxSMeF&q`^at>qC64hdKVE2zV|M8dOc>Hb$%kRGe{p>CrynJ19=N(pkbIE!+ z>t9i_dR;E}=bk&g?Ne=q)#+X3iGeYVeM{ADM7^Fkv7}$!X(GpsYp)L62yMS%zt5=C zUN`ahVy4MPKlMC!wnf~YBHn8Q+R^Zz;r)}7)t4^>Kfii8lS0a5z2iG!$-QUaW#X$`D^|tpO}qW@apbg{8;fTOuUHf|Q{=zFP7gm z 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 Date: Mon, 10 Jun 2024 10:12:50 -0400 Subject: [PATCH 42/43] pqPluginDialog: Use NOLINT for access specifiers correctly --- Qt/Components/pqPluginDialog.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Qt/Components/pqPluginDialog.h b/Qt/Components/pqPluginDialog.h index 37512c3f4ff..193e579e7ae 100644 --- a/Qt/Components/pqPluginDialog.h +++ b/Qt/Components/pqPluginDialog.h @@ -40,7 +40,7 @@ public Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) void loadLocalPlugin(); void loadRemotePlugin(); -protected Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) +protected Q_SLOTS: void onPluginItemChanged(QTreeWidgetItem*, int); void onRefresh(); void refresh(); @@ -70,7 +70,7 @@ protected: // NOLINT(readability-redundant-access-specifiers) void removeSelectedPlugins(QList selItems, pqServer* server, bool remote); QString getStatusText(vtkPVPluginsInformation* plInfo, unsigned int cc); -private Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) +private Q_SLOTS: ///@{ /** * Internal slots called when the a add plugin config file button is pressed @@ -79,7 +79,7 @@ private Q_SLOTS: // NOLINT(readability-redundant-access-specifiers) void onAddPluginConfigLocal(); ///@} -private: +private: // NOLINT(readability-redundant-access-specifiers) ///@{ /** * Internal methods called to add a plugin config file to the plugin manager -- GitLab From 7c76d3ed85909c2434b3350905a863e3b73e6465 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Wed, 12 Jun 2024 14:07:43 -0400 Subject: [PATCH 43/43] VTK: Bump to get composite-reader-writer-assembly-fix * Spiros Tsalikis: composite-reader-writer-assembly-fix (vtk/vtk!11220) * Ben Boeckel: json-fwd-decls (vtk/vtk!11223) * Jaswant Panchumarti (Kitware): speed-up-quick-stage (vtk/vtk!11219) * Florian Maurin: CompleteQuadraticSimplicialElements (vtk/vtk!11214) * Louis Gombert: vtksocket-bind (vtk/vtk!11191) * Jaswant Panchumarti (Kitware): fix-clang-tidy-issues (vtk/vtk!11211) * Caitlin Ross: ensight-gold-reader-rewrite (vtk/vtk!8413) * Tom Clabault: WebGPUComputeAPI (vtk/vtk!11165) --- VTK | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VTK b/VTK index f31c517620e..195292d7eb2 160000 --- a/VTK +++ b/VTK @@ -1 +1 @@ -Subproject commit f31c517620e72a96492a48d483bd3d0b31e83a27 +Subproject commit 195292d7eb29ff6f423dc487ab3852285d7388cc -- GitLab