From 7d6d023d0f0e56eec59383c3ab7c87f0879b1036 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 29 Jul 2026 16:04:19 +0200 Subject: [PATCH 01/20] Type identifier is already used by base class, use Conversion instead --- src/PointDataConversionPlugin.cpp | 78 ++++++++++++++++--------------- src/PointDataConversionPlugin.h | 32 +++++++------ 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index bfa1dd2..fc2cd96 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -12,14 +12,14 @@ Q_PLUGIN_METADATA(IID "studio.manivault.PointDataConversionPlugin") using namespace mv; -const QMap PointDataConversionPlugin::types = QMap({ - { Type::Log2, "Log2" }, - { Type::ArcSin, "Arcsin" } +const QMap PointDataConversionPlugin::CONVERSIONS = QMap({ + { Conversion::Log2, "Log2" }, + { Conversion::ArcSin, "Arcsin" } }); PointDataConversionPlugin::PointDataConversionPlugin(const PluginFactory* factory) : TransformationPlugin(factory), - _type(Type::ArcSin) + _conversion(Conversion::ArcSin) { } @@ -36,20 +36,20 @@ void PointDataConversionPlugin::transform() task.setName("Converting"); task.setRunning(); - task.setProgressDescription(QString("%1 conversion").arg(getTypeName(_type))); + task.setProgressDescription(QString("%1 conversion").arg(getConversionName(_conversion))); points->visitData([this, &points, &task](auto pointData) { std::uint32_t noPointsProcessed = 0; for (auto point : pointData) { for (std::int32_t dimensionIndex = 0; dimensionIndex < points->getNumDimensions(); dimensionIndex++) { - switch (_type) + switch (_conversion) { - case Type::Log2: + case Conversion::Log2: point[dimensionIndex] = std::log2f(point[dimensionIndex] + 1.0f); break; - case Type::ArcSin: + case Conversion::ArcSin: point[dimensionIndex] = std::asinhf(point[dimensionIndex] / 5.0f); break; } @@ -71,22 +71,22 @@ void PointDataConversionPlugin::transform() events().notifyDatasetDataChanged(points); } -PointDataConversionPlugin::Type PointDataConversionPlugin::getType() const +PointDataConversionPlugin::Conversion PointDataConversionPlugin::getConversion() const { - return _type; + return _conversion; } -void PointDataConversionPlugin::setType(const Type& type) +void PointDataConversionPlugin::setConversion(const Conversion& conversion) { - if (type == _type) + if (conversion == _conversion) return; - _type = type; + _conversion = conversion; } -QString PointDataConversionPlugin::getTypeName(const Type& type) +QString PointDataConversionPlugin::getConversionName(const Conversion& conversion) { - return types[type]; + return CONVERSIONS[conversion]; } PointDataConversionPluginFactory::PointDataConversionPluginFactory() : @@ -99,52 +99,53 @@ PointDataConversionPlugin* PointDataConversionPluginFactory::produce() return new PointDataConversionPlugin(this); } + PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(const mv::Datasets& datasets) const { PluginTriggerActions pluginTriggerActions; const auto numberOfDatasets = datasets.count(); - if (PluginFactory::areAllDatasetsOfTheSameType(datasets, PointType)) { - if (numberOfDatasets >= 1 && datasets.first()->getDataType() == PointType) { - const auto addPluginTriggerAction = [this, &pluginTriggerActions, datasets](const PointDataConversionPlugin::Type& type) -> void { - const auto typeName = PointDataConversionPlugin::getTypeName(type); + if (datasets.count() >= 1 && PluginFactory::areAllDatasetsOfTheSameType(datasets, PointType)) { + const auto addPluginTriggerAction = [this, &pluginTriggerActions, datasets](const PointDataConversionPlugin::Conversion& type) -> void { + const auto typeName = PointDataConversionPlugin::getConversionName(type); - auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, QString("Conversion/%1").arg(typeName), QString("Perform %1 data conversion").arg(typeName), icon(), [this, datasets, type](PluginTriggerAction& pluginTriggerAction) -> void { - for (const auto& dataset : datasets) { - auto pluginInstance = dynamic_cast(plugins().requestPlugin(getKind())); + auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, QString("Conversion/%1").arg(typeName), QString("Perform %1 data conversion").arg(typeName), icon(), [this, datasets, type](PluginTriggerAction& pluginTriggerAction) -> void { + for (const auto& dataset : datasets) { + auto pluginInstance = dynamic_cast(plugins().requestPlugin(getKind())); - pluginInstance->setInputDataset(dataset); - pluginInstance->setType(type); - pluginInstance->transform(); - } + pluginInstance->setInputDataset(dataset); + pluginInstance->setConversion(type); + pluginInstance->transform(); + } }); - pluginTriggerActions << pluginTriggerAction; + pluginTriggerActions << pluginTriggerAction; }; - addPluginTriggerAction(PointDataConversionPlugin::Type::Log2); - addPluginTriggerAction(PointDataConversionPlugin::Type::ArcSin); - } + addPluginTriggerAction(PointDataConversionPlugin::Conversion::Log2); + addPluginTriggerAction(PointDataConversionPlugin::Conversion::ArcSin); } return pluginTriggerActions; } + +// This is used in the image viewer PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(const mv::DataTypes& dataTypes) const { PluginTriggerActions pluginTriggerActions; if (dataTypes.count(PointType) == dataTypes.count()) { - const auto addPluginTriggerAction = [this, &pluginTriggerActions](const PointDataConversionPlugin::Type& type) -> void { - const auto typeName = PointDataConversionPlugin::getTypeName(type); + const auto addPluginTriggerAction = [this, &pluginTriggerActions](const PointDataConversionPlugin::Conversion& type) -> void { + const auto typeName = PointDataConversionPlugin::getConversionName(type); auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, QString("Conversion/%1").arg(typeName), QString("Perform %1 data conversion").arg(typeName), icon(), [this, type](PluginTriggerAction& pluginTriggerAction) -> void { for (const auto& dataset : pluginTriggerAction.getDatasets()) { auto pluginInstance = dynamic_cast(plugins().requestPlugin(getKind())); pluginInstance->setInputDataset(dataset); - pluginInstance->setType(type); + pluginInstance->setConversion(type); pluginInstance->transform(); } }); @@ -154,14 +155,15 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c pluginTriggerActions << pluginTriggerAction; }; - addPluginTriggerAction(PointDataConversionPlugin::Type::Log2); - addPluginTriggerAction(PointDataConversionPlugin::Type::ArcSin); + addPluginTriggerAction(PointDataConversionPlugin::Conversion::Log2); + addPluginTriggerAction(PointDataConversionPlugin::Conversion::ArcSin); } return pluginTriggerActions; } -WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Type& type) +// TODO: actually use the cofactor +WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Conversion& type) { const auto createGroupAction = [this](WidgetAction& widgetAction) -> GroupAction* { auto groupAction = new GroupAction(this, "PointDataConversionGroupAction"); @@ -176,10 +178,10 @@ WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const Poi switch (type) { - case PointDataConversionPlugin::Type::Log2: + case PointDataConversionPlugin::Conversion::Log2: return nullptr; - case PointDataConversionPlugin::Type::ArcSin: + case PointDataConversionPlugin::Conversion::ArcSin: return createGroupAction(_arcSinFactorAction); } diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index 6745b59..d9b3a69 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -1,9 +1,15 @@ #pragma once -#include #include + +#include #include +#include +#include + +#include + using namespace mv::plugin; using namespace mv::gui; using namespace mv::util; @@ -20,12 +26,12 @@ class PointDataConversionPlugin : public TransformationPlugin public: /** Point data conversion type */ - enum class Type { + enum class Conversion { Log2, /** log2(value+1) */ ArcSin /** asinh(value/factor) */ }; - static const QMap types; + static const QMap CONVERSIONS; public: @@ -44,28 +50,28 @@ class PointDataConversionPlugin : public TransformationPlugin /** Performs the data transformation */ void transform() override; - /** /** * Get point data conversion type * @return Point data conversion type */ - Type getType() const; + Conversion getConversion() const; /** * Set point data conversion type - * @param type Point data conversion type + * @param conversion Point data conversion type */ - void setType(const Type& type); + void setConversion(const Conversion& conversion); /** * Get string representation of type enum - * @param type Point data conversion type - * @return Type name + * @param conversion Point data conversion type + * @return conversion name */ - static QString getTypeName(const Type& type); + static QString getConversionName(const Conversion& conversion); private: - Type _type; /** Data conversion type */ + Conversion _conversion; /** Data conversion type */ + std::vector _cofactors; }; /** @@ -100,7 +106,7 @@ class PointDataConversionPluginFactory : public TransformationPluginFactory /** * Get plugin trigger actions given \p dataTypes - * @param datasetTypes Vector of input data types + * @param dataTypes Vector of input data types * @return Vector of plugin trigger actions */ PluginTriggerActions getPluginTriggerActions(const mv::DataTypes& dataTypes) const override; @@ -109,7 +115,7 @@ class PointDataConversionPluginFactory : public TransformationPluginFactory * Get configuration action for \p type * @return Pointer to configuration action (may be null) */ - WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Type& type); + WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Conversion& type); private: DecimalAction _arcSinFactorAction; /** Factor for arcsin(value/factor) conversion */ From e9af06c342b5aed437c225aaa7c562739faa144b Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 29 Jul 2026 16:05:00 +0200 Subject: [PATCH 02/20] Update point data API --- src/PointDataConversionPlugin.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index fc2cd96..e5285d1 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -39,10 +39,9 @@ void PointDataConversionPlugin::transform() task.setProgressDescription(QString("%1 conversion").arg(getConversionName(_conversion))); points->visitData([this, &points, &task](auto pointData) { - std::uint32_t noPointsProcessed = 0; - + std::uint64_t noPointsProcessed = 0; for (auto point : pointData) { - for (std::int32_t dimensionIndex = 0; dimensionIndex < points->getNumDimensions(); dimensionIndex++) { + for (std::uint64_t dimensionIndex = 0; dimensionIndex < points->getNumDimensions(); dimensionIndex++) { switch (_conversion) { case Conversion::Log2: From 4c4e5b26778f927a8d80c72cc47a1fff6cdd9910 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 29 Jul 2026 16:18:05 +0200 Subject: [PATCH 03/20] Fewer using namespaces in header --- src/PointDataConversionPlugin.cpp | 6 ++---- src/PointDataConversionPlugin.h | 15 ++++++--------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index e5285d1..37dcb44 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -11,13 +11,14 @@ Q_PLUGIN_METADATA(IID "studio.manivault.PointDataConversionPlugin") using namespace mv; +using namespace mv::gui; const QMap PointDataConversionPlugin::CONVERSIONS = QMap({ { Conversion::Log2, "Log2" }, { Conversion::ArcSin, "Arcsin" } }); -PointDataConversionPlugin::PointDataConversionPlugin(const PluginFactory* factory) : +PointDataConversionPlugin::PointDataConversionPlugin(const mv::plugin::PluginFactory* factory) : TransformationPlugin(factory), _conversion(Conversion::ArcSin) { @@ -103,8 +104,6 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c { PluginTriggerActions pluginTriggerActions; - const auto numberOfDatasets = datasets.count(); - if (datasets.count() >= 1 && PluginFactory::areAllDatasetsOfTheSameType(datasets, PointType)) { const auto addPluginTriggerAction = [this, &pluginTriggerActions, datasets](const PointDataConversionPlugin::Conversion& type) -> void { const auto typeName = PointDataConversionPlugin::getConversionName(type); @@ -129,7 +128,6 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c return pluginTriggerActions; } - // This is used in the image viewer PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(const mv::DataTypes& dataTypes) const { diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index d9b3a69..785c76e 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -10,16 +10,13 @@ #include -using namespace mv::plugin; -using namespace mv::gui; -using namespace mv::util; /** * Point data conversion plugin class * * @author Thomas Kroes */ -class PointDataConversionPlugin : public TransformationPlugin +class PointDataConversionPlugin : public mv::plugin::TransformationPlugin { Q_OBJECT @@ -39,7 +36,7 @@ class PointDataConversionPlugin : public TransformationPlugin * Constructor * @param factory Pointer to the plugin factory */ - PointDataConversionPlugin(const PluginFactory* factory); + PointDataConversionPlugin(const mv::plugin::PluginFactory* factory); /** Destructor */ ~PointDataConversionPlugin() override = default; @@ -79,7 +76,7 @@ class PointDataConversionPlugin : public TransformationPlugin * * @author Thomas Kroes */ -class PointDataConversionPluginFactory : public TransformationPluginFactory +class PointDataConversionPluginFactory : public mv::plugin::TransformationPluginFactory { Q_INTERFACES(mv::plugin::TransformationPluginFactory mv::plugin::PluginFactory) Q_OBJECT @@ -102,14 +99,14 @@ class PointDataConversionPluginFactory : public TransformationPluginFactory * @param datasets Vector of input datasets * @return Vector of plugin trigger actions */ - PluginTriggerActions getPluginTriggerActions(const mv::Datasets& datasets) const override; + mv::gui::PluginTriggerActions getPluginTriggerActions(const mv::Datasets& datasets) const override; /** * Get plugin trigger actions given \p dataTypes * @param dataTypes Vector of input data types * @return Vector of plugin trigger actions */ - PluginTriggerActions getPluginTriggerActions(const mv::DataTypes& dataTypes) const override; + mv::gui::PluginTriggerActions getPluginTriggerActions(const mv::DataTypes& dataTypes) const override; /** * Get configuration action for \p type @@ -118,5 +115,5 @@ class PointDataConversionPluginFactory : public TransformationPluginFactory WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Conversion& type); private: - DecimalAction _arcSinFactorAction; /** Factor for arcsin(value/factor) conversion */ + mv::gui::DecimalAction _arcSinFactorAction; /** Factor for arcsin(value/factor) conversion */ }; From 09bd2e20028029f5c64451f639d0819b614cd432 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 29 Jul 2026 16:18:20 +0200 Subject: [PATCH 04/20] Refactor looping over data --- src/PointDataConversionPlugin.cpp | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index 37dcb44..8da0fd5 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -20,7 +20,8 @@ const QMap PointDataConversionPl PointDataConversionPlugin::PointDataConversionPlugin(const mv::plugin::PluginFactory* factory) : TransformationPlugin(factory), - _conversion(Conversion::ArcSin) + _conversion(Conversion::ArcSin), + _cofactors({5.f}) { } @@ -41,28 +42,33 @@ void PointDataConversionPlugin::transform() points->visitData([this, &points, &task](auto pointData) { std::uint64_t noPointsProcessed = 0; - for (auto point : pointData) { + + float cofactor = _cofactors[0]; + + // TODO: parallelize the outer loop + for (std::uint64_t pointIndex = 0; pointIndex < points->getNumPoints(); pointIndex++) { + for (std::uint64_t dimensionIndex = 0; dimensionIndex < points->getNumDimensions(); dimensionIndex++) { switch (_conversion) { - case Conversion::Log2: - point[dimensionIndex] = std::log2f(point[dimensionIndex] + 1.0f); - break; - - case Conversion::ArcSin: - point[dimensionIndex] = std::asinhf(point[dimensionIndex] / 5.0f); - break; + case Conversion::Log2: + pointData[pointIndex][dimensionIndex] = std::log2f(pointData[pointIndex][dimensionIndex] + 1.0f); + break; + + case Conversion::ArcSin: + pointData[pointIndex][dimensionIndex] = std::asinhf(pointData[pointIndex][dimensionIndex] / cofactor); + break; } } - - ++noPointsProcessed; - - if (noPointsProcessed % 1000 == 0) { + + // TODO: guard this when parallelizing + if (++noPointsProcessed % 1000 == 0) { task.setProgress(static_cast(noPointsProcessed) / static_cast(points->getNumPoints())); - + QApplication::processEvents(); } } + }); task.setProgress(1.0f); @@ -99,7 +105,7 @@ PointDataConversionPlugin* PointDataConversionPluginFactory::produce() return new PointDataConversionPlugin(this); } - +// TODO: add gui to optionally set per-channel cofactor PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(const mv::Datasets& datasets) const { PluginTriggerActions pluginTriggerActions; From 49a7e995e9f4cc15e412372b95de15338d0e2189 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 14:58:43 +0200 Subject: [PATCH 05/20] Update CI dependencies --- .github/workflows/build.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 325dcf1..11a772e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,10 +5,6 @@ on: pull_request: workflow_dispatch: -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - # for matrix check https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners jobs: prepare_matrix: @@ -35,14 +31,14 @@ jobs: steps: - name: Checkout the source if: github.event_name != 'pull_request' - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive fetch-depth: 0 - name: Checkout the source - pull request if: github.event_name == 'pull_request' - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive fetch-depth: 0 @@ -59,7 +55,7 @@ jobs: python-version: "3.12" - name: Start ssh key agent - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.10.0 with: ssh-private-key: ${{ secrets.RULESSUPPORT_DEPLOY_KEY }} From 4ac1d306d68f50a0e13eb0f7796868db258a4641 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 15:00:03 +0200 Subject: [PATCH 06/20] Parallel transform --- CMakeLists.txt | 11 ++++++++--- conanfile.py | 18 ++++++++++++++---- src/PointDataConversionPlugin.cpp | 26 ++++++++++++++++---------- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 37b66ea..3ceedd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,12 +13,10 @@ PROJECT(${PROJECT} # ----------------------------------------------------------------------------- # CMake Options # ----------------------------------------------------------------------------- -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC ON) if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DWIN32 /EHsc /MP /permissive- /Zc:__cplusplus") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DWIN32 /EHsc /MP /Zc:__cplusplus") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MD") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") @@ -31,6 +29,8 @@ find_package(Qt6 COMPONENTS Widgets WebEngineWidgets REQUIRED) find_package(ManiVault COMPONENTS Core PointData CONFIG QUIET) +find_package(OpenMP) + # ----------------------------------------------------------------------------- # Source files # ----------------------------------------------------------------------------- @@ -60,6 +60,10 @@ target_include_directories(${PROJECT} PRIVATE "${ManiVault_INCLUDE_DIR}") # ----------------------------------------------------------------------------- target_compile_features(${PROJECT} PRIVATE cxx_std_20) +set_target_properties(${PROJECT} PROPERTIES + AUTOMOC ON +) + # ----------------------------------------------------------------------------- # Target library linking # ----------------------------------------------------------------------------- @@ -68,6 +72,7 @@ target_link_libraries(${PROJECT} PRIVATE Qt6::WebEngineWidgets) target_link_libraries(${PROJECT} PRIVATE ManiVault::Core) target_link_libraries(${PROJECT} PRIVATE ManiVault::PointData) +target_link_libraries(${PROJECT} PRIVATE OpenMP::OpenMP_CXX) # ----------------------------------------------------------------------------- # Target installation diff --git a/conanfile.py b/conanfile.py index f1b7ff6..2321a84 100644 --- a/conanfile.py +++ b/conanfile.py @@ -9,7 +9,7 @@ class PointDataConversionPluginConan(ConanFile): - """Class to package the PointDataConversionPlugin using conan + """Class to package the PointDataConversionPlugin using conan Packages both RELEASE and RELWITHDEBINFO. Uses rules_support (github.com/ManiVaultStudio/rulessupport) to derive @@ -68,8 +68,13 @@ def configure(self): pass def system_requirements(self): - # May be needed for macOS or Linux - pass + if os_info.is_macos: + installer = SystemPackageTool() + installer.install("libomp") + proc = subprocess.run("brew --prefix libomp", shell=True, capture_output=True) + subprocess.run(f"ln {proc.stdout.decode('UTF-8').strip()}/lib/libomp.dylib /usr/local/lib/libomp.dylib", shell=True) + if os_info.is_linux: + self.run("sudo apt update && sudo apt install -y libtbb-dev") def config_options(self): if self.settings.os == "Windows": @@ -100,8 +105,13 @@ def generate(self): tc.variables["ManiVault_DIR"] = manivault_dir # Set some build options - tc.variables["MV_UNITY_BUILD"] = "ON" + tc.cache_variables["MV_UNITY_BUILD"] = "ON" + if os_info.is_macos: + proc = subprocess.run("brew --prefix libomp", shell=True, capture_output=True) + prefix_path = f"{proc.stdout.decode('UTF-8').strip()}" + tc.variables["OpenMP_ROOT"] = prefix_path + tc.generate() def _configure_cmake(self): diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index 8da0fd5..eaaaddf 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -4,6 +4,7 @@ #include +#include #include #include @@ -41,14 +42,17 @@ void PointDataConversionPlugin::transform() task.setProgressDescription(QString("%1 conversion").arg(getConversionName(_conversion))); points->visitData([this, &points, &task](auto pointData) { - std::uint64_t noPointsProcessed = 0; + std::atomic_uint64_t noPointsProcessed = 0; - float cofactor = _cofactors[0]; + float cofactor = _cofactors[0]; + const auto numPointsF = static_cast(points->getNumPoints()); + const auto numPointsI = static_cast(points->getNumPoints()); + const auto numDims = points->getNumDimensions(); - // TODO: parallelize the outer loop - for (std::uint64_t pointIndex = 0; pointIndex < points->getNumPoints(); pointIndex++) { +#pragma omp parallel for + for (std::int64_t pointIndex = 0; pointIndex < numPointsI; pointIndex++) { - for (std::uint64_t dimensionIndex = 0; dimensionIndex < points->getNumDimensions(); dimensionIndex++) { + for (std::uint64_t dimensionIndex = 0; dimensionIndex < numDims; dimensionIndex++) { switch (_conversion) { case Conversion::Log2: @@ -61,11 +65,13 @@ void PointDataConversionPlugin::transform() } } - // TODO: guard this when parallelizing - if (++noPointsProcessed % 1000 == 0) { - task.setProgress(static_cast(noPointsProcessed) / static_cast(points->getNumPoints())); - - QApplication::processEvents(); + if (const auto processed = ++noPointsProcessed; + processed % 1000 == 0) { +#pragma omp critical + { + task.setProgress(static_cast(noPointsProcessed) / numPointsF); + QApplication::processEvents(); + } } } From ea4159f788e32f9d0665482721e13764f2644d0d Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 15:00:17 +0200 Subject: [PATCH 07/20] Add setter --- src/PointDataConversionPlugin.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index 785c76e..af4a1af 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -47,6 +47,12 @@ class PointDataConversionPlugin : public mv::plugin::TransformationPlugin /** Performs the data transformation */ void transform() override; + /** Set the sinh cofactors */ + void setCofactor(const std::vector& cofactors) { _cofactors = cofactors; } + + /** Set the sinh cofactors */ + void setCofactor(std::vector cofactors) { _cofactors = std::move(cofactors); } + /** * Get point data conversion type * @return Point data conversion type From 62de9a0224505ed0c977d639ef46d76eb26dd1e0 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 15:05:10 +0200 Subject: [PATCH 08/20] Add conan import --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 2321a84..d118966 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,6 +1,7 @@ from conans import ConanFile from conan.tools.cmake import CMakeDeps, CMake, CMakeToolchain from conans.tools import save, load +from conans.tools import os_info, SystemPackageTool import os import shutil import pathlib From 61701cd8f56bb238b1086acdde08c956ca7d177c Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 15:12:38 +0200 Subject: [PATCH 09/20] Avoid confusion --- src/PointDataConversionPlugin.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index af4a1af..936b42b 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -47,9 +47,6 @@ class PointDataConversionPlugin : public mv::plugin::TransformationPlugin /** Performs the data transformation */ void transform() override; - /** Set the sinh cofactors */ - void setCofactor(const std::vector& cofactors) { _cofactors = cofactors; } - /** Set the sinh cofactors */ void setCofactor(std::vector cofactors) { _cofactors = std::move(cofactors); } From 3ef530fce2f0bee197ff9cd4f947406fe6151f62 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 15:14:50 +0200 Subject: [PATCH 10/20] Use cofactor for getPluginTriggerActions(const mv::DataTypes& dataTypes) --- src/PointDataConversionPlugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index eaaaddf..c073cbf 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -155,6 +155,8 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c pluginInstance->setInputDataset(dataset); pluginInstance->setConversion(type); + pluginInstance->setCofactor(std::vector{ _arcSinFactorAction.getValue() }); + pluginInstance->transform(); } }); @@ -171,7 +173,6 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c return pluginTriggerActions; } -// TODO: actually use the cofactor WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Conversion& type) { const auto createGroupAction = [this](WidgetAction& widgetAction) -> GroupAction* { From 48a0cda54fd923c5f5d763319c826604d11a7001 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 15:26:05 +0200 Subject: [PATCH 11/20] Introduce cofactor helper function --- src/PointDataConversionPlugin.cpp | 7 ++++++- src/PointDataConversionPlugin.h | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index c073cbf..d62afc8 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -111,6 +111,11 @@ PointDataConversionPlugin* PointDataConversionPluginFactory::produce() return new PointDataConversionPlugin(this); } +std::vector PointDataConversionPluginFactory::getArcSinCoFactor() const +{ + return { _arcSinFactorAction.getValue() }; +} + // TODO: add gui to optionally set per-channel cofactor PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(const mv::Datasets& datasets) const { @@ -155,7 +160,7 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c pluginInstance->setInputDataset(dataset); pluginInstance->setConversion(type); - pluginInstance->setCofactor(std::vector{ _arcSinFactorAction.getValue() }); + pluginInstance->setCofactor(getArcSinCoFactor()); pluginInstance->transform(); } diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index 936b42b..670c59a 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -42,7 +42,7 @@ class PointDataConversionPlugin : public mv::plugin::TransformationPlugin ~PointDataConversionPlugin() override = default; /** Initialization is called when the plugin is first instantiated. */ - void init() override {}; + void init() override {} /** Performs the data transformation */ void transform() override; @@ -117,6 +117,9 @@ class PointDataConversionPluginFactory : public mv::plugin::TransformationPlugin */ WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Conversion& type); +private: + std::vector getArcSinCoFactor() const; + private: mv::gui::DecimalAction _arcSinFactorAction; /** Factor for arcsin(value/factor) conversion */ }; From 7faec2f9deb0efc93f3278ef41d20a8f472824ff Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 16:33:54 +0200 Subject: [PATCH 12/20] Draft multiple sliders action --- CMakeLists.txt | 2 + src/PointDataConversionPlugin.cpp | 23 ++++++- src/PointDataConversionPlugin.h | 10 +-- src/SlidersAction.cpp | 111 ++++++++++++++++++++++++++++++ src/SlidersAction.h | 50 ++++++++++++++ 5 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 src/SlidersAction.cpp create mode 100644 src/SlidersAction.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ceedd1..d29e075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,8 @@ find_package(OpenMP) set(SOURCES src/PointDataConversionPlugin.h src/PointDataConversionPlugin.cpp + src/SlidersAction.h + src/SlidersAction.cpp ) set(AUX diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index d62afc8..099653f 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -14,6 +14,10 @@ Q_PLUGIN_METADATA(IID "studio.manivault.PointDataConversionPlugin") using namespace mv; using namespace mv::gui; +// ============================================================================= +// PointDataConversionPlugin +// ============================================================================= + const QMap PointDataConversionPlugin::CONVERSIONS = QMap({ { Conversion::Log2, "Log2" }, { Conversion::ArcSin, "Arcsin" } @@ -101,9 +105,18 @@ QString PointDataConversionPlugin::getConversionName(const Conversion& conversio return CONVERSIONS[conversion]; } +// ============================================================================= +// PointDataConversionPluginFactory +// ============================================================================= + PointDataConversionPluginFactory::PointDataConversionPluginFactory() : - _arcSinFactorAction(this, "Factor", 1.0f, 100.0f, 5.0f, 5.0f) + _arcSinFactorAction(this, "Factor", 1.0f, 100.0f, 5.0f, 2), + _arcSinFactorsAction(this, "Factors") { + QStringList opts = { "1", "2", "3" }; + _arcSinFactorsAction.setOptions(opts); + for (const auto& opt : opts) + _arcSinFactorsAction.setDataForOption(opt, 5.f, 0.f, 100.f); } PointDataConversionPlugin* PointDataConversionPluginFactory::produce() @@ -113,7 +126,11 @@ PointDataConversionPlugin* PointDataConversionPluginFactory::produce() std::vector PointDataConversionPluginFactory::getArcSinCoFactor() const { - return { _arcSinFactorAction.getValue() }; + if (_sameCofactor) + return { _arcSinFactorAction.getValue() }; + + return { _arcSinFactorsAction.getValues() }; + } // TODO: add gui to optionally set per-channel cofactor @@ -197,7 +214,7 @@ WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const Poi return nullptr; case PointDataConversionPlugin::Conversion::ArcSin: - return createGroupAction(_arcSinFactorAction); + return createGroupAction(_arcSinFactorsAction); } return nullptr; diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index 670c59a..b020bb3 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -5,12 +5,12 @@ #include #include -#include -#include +#include "SlidersAction.h" +#include +#include #include - /** * Point data conversion plugin class * @@ -121,5 +121,7 @@ class PointDataConversionPluginFactory : public mv::plugin::TransformationPlugin std::vector getArcSinCoFactor() const; private: - mv::gui::DecimalAction _arcSinFactorAction; /** Factor for arcsin(value/factor) conversion */ + bool _sameCofactor = true; + mv::gui::DecimalAction _arcSinFactorAction; /** Factor for arcsin(value/factor) conversion */ + mv::gui::SlidersAction _arcSinFactorsAction; /** Factor for arcsin(value/factor) conversion */ }; diff --git a/src/SlidersAction.cpp b/src/SlidersAction.cpp new file mode 100644 index 0000000..65e45ae --- /dev/null +++ b/src/SlidersAction.cpp @@ -0,0 +1,111 @@ +#include "SlidersAction.h" + +#include + +#include +#include +#include + +using namespace mv::gui; + +SlidersAction::SlidersAction(QObject* parent, const QString& title) : + WidgetAction(parent, title) +{ + setText(title); + setDefaultWidgetFlags(SlidersAction::Default); +} + +void SlidersAction::initialize(const QStringList& options) { + setOptions(options); +} + +void SlidersAction::setOptions(const QStringList& options) { + _options = options; + // populate default OptionData if new + for (const QString& opt : options) { + if (!_optionData.contains(opt)) { + _optionData.insert({ opt, OptionData{} }); + } + } + // remove data for removed options + for (auto it = _optionData.begin(); it != _optionData.end(); ) { + if (!options.contains(it->first)) + it = _optionData.erase(it); + else ++it; + } +} + +void SlidersAction::setRangeForOption(const QString& option, float minimum, float maximum) { + if (!_optionData.contains(option)) return; + auto& d = _optionData[option]; + d.min = minimum; + d.max = maximum; + d.value = std::clamp(d.value, d.min, d.max); +} + +void SlidersAction::setValueForOption(const QString& option, float value) { + if (!_optionData.contains(option)) return; + auto& d = _optionData[option]; + value = std::clamp(value, d.min, d.max); + if (d.value == value) return; + d.value = value; + emit optionValueChanged(option, value); +} + +void SlidersAction::setDataForOption(const QString& option, float value, float minimum, float maximum) { + if (!_optionData.contains(option)) return; + auto& d = _optionData[option]; + d.min = minimum; + d.max = maximum; + value = std::clamp(value, d.min, d.max); + if (d.value == value) return; + d.value = value; + emit optionValueChanged(option, value); +} + +float SlidersAction::getValueForOption(const QString& option) const { + if (!_optionData.contains(option)) return 0.0f; + return _optionData.at(option).value; +} + +std::vector SlidersAction::getValues() const +{ + std::vector values; + values.reserve(_options.size()); + + for (const auto& opt : _options) + values.push_back(getValueForOption(opt)); + + return values; +} + +QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFlags) { + // Create a popup list widget with rows; each row contains a checkbox, label, slider, spinbox. + auto* container = new QWidget(parent); + auto* layout = new QVBoxLayout(container); + layout->setContentsMargins(4, 4, 4, 4); + + auto* list = new QListWidget(container); + list->setSelectionMode(QAbstractItemView::NoSelection); + layout->addWidget(list); + + for (const QString& opt : _options) { + auto* item = new QListWidgetItem(list); + + QWidget* row = new QWidget(list); + QHBoxLayout* rowLayout = new QHBoxLayout(row); + rowLayout->setContentsMargins(2, 2, 2, 2); + + const OptionData& d = _optionData.at(opt); + + DecimalAction* slider = new DecimalAction(this, opt, d.min, d.max, d.value, 2); + rowLayout->addWidget(slider->createLabelWidget(container)); + rowLayout->addWidget(slider->createWidget(container)); + + list->addItem(item); + list->setItemWidget(item, row); + item->setSizeHint(row->sizeHint()); + } + + return container; +} diff --git a/src/SlidersAction.h b/src/SlidersAction.h new file mode 100644 index 0000000..64c744b --- /dev/null +++ b/src/SlidersAction.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include + +namespace mv::gui { + + class SlidersAction : public WidgetAction { + Q_OBJECT + public: + explicit SlidersAction(QObject* parent, const QString& title); + + /** Describes the widget flags */ + enum WidgetFlag { + Default = 0x00001, + }; + + void initialize(const QStringList& options = QStringList()); + void setOptions(const QStringList& options); + + void setRangeForOption(const QString& option, float minimum, float maximum); + void setValueForOption(const QString& option, float value); + void setDataForOption(const QString& option, float value, float minimum, float maximum); + [[nodiscard]] float getValueForOption(const QString& option) const; + [[nodiscard]] std::vector getValues() const; + + signals: + void optionValueChanged(const QString& option, float value); + + protected: + QWidget* getWidget(QWidget* parent, const std::int32_t& widgetFlags) override; + + private: + struct OptionData { + float min = 0.0f; + float max = 1.0f; + float value = 0.5f; + }; + + QStringList _options = {}; + std::unordered_map _optionData = {}; + }; +} From c6e79055b6e07ba250b10d477c9f9a5773e5154e Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 17:23:39 +0200 Subject: [PATCH 13/20] Refacor sliders and save new values --- src/SlidersAction.cpp | 37 ++++++++++++++++++++----------------- src/SlidersAction.h | 23 ++++++++++++++++------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/SlidersAction.cpp b/src/SlidersAction.cpp index 65e45ae..61daebb 100644 --- a/src/SlidersAction.cpp +++ b/src/SlidersAction.cpp @@ -35,7 +35,8 @@ void SlidersAction::setOptions(const QStringList& options) { } } -void SlidersAction::setRangeForOption(const QString& option, float minimum, float maximum) { +void SlidersAction::setRangeForOption(const QString& option, float minimum, float maximum) +{ if (!_optionData.contains(option)) return; auto& d = _optionData[option]; d.min = minimum; @@ -43,28 +44,26 @@ void SlidersAction::setRangeForOption(const QString& option, float minimum, floa d.value = std::clamp(d.value, d.min, d.max); } -void SlidersAction::setValueForOption(const QString& option, float value) { +void SlidersAction::setValueForOption(const QString& option, float value) +{ if (!_optionData.contains(option)) return; auto& d = _optionData[option]; value = std::clamp(value, d.min, d.max); - if (d.value == value) return; + if (std::abs(d.value - value) < 0.0001f) return; d.value = value; emit optionValueChanged(option, value); } -void SlidersAction::setDataForOption(const QString& option, float value, float minimum, float maximum) { - if (!_optionData.contains(option)) return; - auto& d = _optionData[option]; - d.min = minimum; - d.max = maximum; - value = std::clamp(value, d.min, d.max); - if (d.value == value) return; - d.value = value; - emit optionValueChanged(option, value); +void SlidersAction::setDataForOption(const QString& option, float value, float minimum, float maximum) +{ + setRangeForOption(option, minimum, maximum); + setValueForOption(option, value); } -float SlidersAction::getValueForOption(const QString& option) const { - if (!_optionData.contains(option)) return 0.0f; +float SlidersAction::getValueForOption(const QString& option) const +{ + if (!_optionData.contains(option)) + return 0.0f; return _optionData.at(option).value; } @@ -79,8 +78,8 @@ std::vector SlidersAction::getValues() const return values; } -QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFlags) { - // Create a popup list widget with rows; each row contains a checkbox, label, slider, spinbox. +QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFlags) +{ auto* container = new QWidget(parent); auto* layout = new QVBoxLayout(container); layout->setContentsMargins(4, 4, 4, 4); @@ -98,13 +97,17 @@ QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFla const OptionData& d = _optionData.at(opt); - DecimalAction* slider = new DecimalAction(this, opt, d.min, d.max, d.value, 2); + DecimalAction* slider = new DecimalAction(this, opt, d.min, d.max, d.value, OptionData::DEFAULT_DECIMALS); rowLayout->addWidget(slider->createLabelWidget(container)); rowLayout->addWidget(slider->createWidget(container)); list->addItem(item); list->setItemWidget(item, row); item->setSizeHint(row->sizeHint()); + + connect(slider, &DecimalAction::valueChanged, this, [this, opt](float value) { + setValueForOption(opt, value); + }); } return container; diff --git a/src/SlidersAction.h b/src/SlidersAction.h index 64c744b..1e340f5 100644 --- a/src/SlidersAction.h +++ b/src/SlidersAction.h @@ -14,14 +14,28 @@ namespace mv::gui { class SlidersAction : public WidgetAction { Q_OBJECT - public: - explicit SlidersAction(QObject* parent, const QString& title); + public: /** Describes the widget flags */ enum WidgetFlag { Default = 0x00001, }; + public: + struct OptionData { + static constexpr float DEFAULT_MIN = 1.0f; + static constexpr float DEFAULT_MAX = 100.0f; + static constexpr float DEFAULT_VALUE = 5.0f; + static constexpr std::int32_t DEFAULT_DECIMALS = 2; + + float min = DEFAULT_MIN; + float max = DEFAULT_MAX; + float value = DEFAULT_VALUE; + }; + + public: + explicit SlidersAction(QObject* parent, const QString& title); + void initialize(const QStringList& options = QStringList()); void setOptions(const QStringList& options); @@ -38,11 +52,6 @@ namespace mv::gui { QWidget* getWidget(QWidget* parent, const std::int32_t& widgetFlags) override; private: - struct OptionData { - float min = 0.0f; - float max = 1.0f; - float value = 0.5f; - }; QStringList _options = {}; std::unordered_map _optionData = {}; From 6c971f3897be0455344cae35cb02eea672ac2b88 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 17:49:37 +0200 Subject: [PATCH 14/20] Enable & disable all children --- src/SlidersAction.cpp | 31 +++++++++++++++++++++++-------- src/SlidersAction.h | 5 ++++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/SlidersAction.cpp b/src/SlidersAction.cpp index 61daebb..f0e48e7 100644 --- a/src/SlidersAction.cpp +++ b/src/SlidersAction.cpp @@ -4,7 +4,6 @@ #include #include -#include using namespace mv::gui; @@ -78,20 +77,36 @@ std::vector SlidersAction::getValues() const return values; } +void SlidersAction::setAllSlidersEnabled(bool enabled) +{ + setEnabled(enabled); + + if (!_sliderList) + return; + + for (int i = 0; i < _sliderList->count(); ++i) { + QListWidgetItem* item = _sliderList->item(i); + if (QWidget* row = _sliderList->itemWidget(item)) { + row->setEnabled(enabled); + } + } + +} + QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFlags) { auto* container = new QWidget(parent); auto* layout = new QVBoxLayout(container); layout->setContentsMargins(4, 4, 4, 4); - auto* list = new QListWidget(container); - list->setSelectionMode(QAbstractItemView::NoSelection); - layout->addWidget(list); + _sliderList = new QListWidget(container); + _sliderList->setSelectionMode(QAbstractItemView::NoSelection); + layout->addWidget(_sliderList); for (const QString& opt : _options) { - auto* item = new QListWidgetItem(list); + auto* item = new QListWidgetItem(_sliderList); - QWidget* row = new QWidget(list); + QWidget* row = new QWidget(_sliderList); QHBoxLayout* rowLayout = new QHBoxLayout(row); rowLayout->setContentsMargins(2, 2, 2, 2); @@ -101,8 +116,8 @@ QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFla rowLayout->addWidget(slider->createLabelWidget(container)); rowLayout->addWidget(slider->createWidget(container)); - list->addItem(item); - list->setItemWidget(item, row); + _sliderList->addItem(item); + _sliderList->setItemWidget(item, row); item->setSizeHint(row->sizeHint()); connect(slider, &DecimalAction::valueChanged, this, [this, opt](float value) { diff --git a/src/SlidersAction.h b/src/SlidersAction.h index 1e340f5..bae21c2 100644 --- a/src/SlidersAction.h +++ b/src/SlidersAction.h @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -45,6 +46,8 @@ namespace mv::gui { [[nodiscard]] float getValueForOption(const QString& option) const; [[nodiscard]] std::vector getValues() const; + void setAllSlidersEnabled(bool enabled); + signals: void optionValueChanged(const QString& option, float value); @@ -52,8 +55,8 @@ namespace mv::gui { QWidget* getWidget(QWidget* parent, const std::int32_t& widgetFlags) override; private: - QStringList _options = {}; std::unordered_map _optionData = {}; + QListWidget* _sliderList = nullptr; }; } From b657d5a9c89f66c3226d44657ab8963d10bbe96a Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 17:53:12 +0200 Subject: [PATCH 15/20] Add sliders to plugin --- src/PointDataConversionPlugin.cpp | 31 +++++++++++++++++++++---------- src/PointDataConversionPlugin.h | 11 ++++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index 099653f..2fce78f 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -24,9 +24,7 @@ const QMap PointDataConversionPl }); PointDataConversionPlugin::PointDataConversionPlugin(const mv::plugin::PluginFactory* factory) : - TransformationPlugin(factory), - _conversion(Conversion::ArcSin), - _cofactors({5.f}) + TransformationPlugin(factory) { } @@ -53,6 +51,8 @@ void PointDataConversionPlugin::transform() const auto numPointsI = static_cast(points->getNumPoints()); const auto numDims = points->getNumDimensions(); + qDebug() << "PointDataConversionPlugin::transform: cofactor of" << cofactor ; + #pragma omp parallel for for (std::int64_t pointIndex = 0; pointIndex < numPointsI; pointIndex++) { @@ -110,13 +110,23 @@ QString PointDataConversionPlugin::getConversionName(const Conversion& conversio // ============================================================================= PointDataConversionPluginFactory::PointDataConversionPluginFactory() : - _arcSinFactorAction(this, "Factor", 1.0f, 100.0f, 5.0f, 2), + _sameFactorAction(this, "Same factor", true), + _arcSinFactorAction(this, "Factor", + SlidersAction::OptionData::DEFAULT_MIN, SlidersAction::OptionData::DEFAULT_MAX, + SlidersAction::OptionData::DEFAULT_VALUE, SlidersAction::OptionData::DEFAULT_DECIMALS), _arcSinFactorsAction(this, "Factors") { QStringList opts = { "1", "2", "3" }; _arcSinFactorsAction.setOptions(opts); for (const auto& opt : opts) _arcSinFactorsAction.setDataForOption(opt, 5.f, 0.f, 100.f); + + connect(&_sameFactorAction, &ToggleAction::toggled, this, [&](bool toggled) + { + _arcSinFactorAction.setEnabled(_sameFactorAction.isChecked()); + _arcSinFactorsAction.setAllSlidersEnabled(!_sameFactorAction.isChecked()); + }); + } PointDataConversionPlugin* PointDataConversionPluginFactory::produce() @@ -126,11 +136,10 @@ PointDataConversionPlugin* PointDataConversionPluginFactory::produce() std::vector PointDataConversionPluginFactory::getArcSinCoFactor() const { - if (_sameCofactor) + if (_sameFactorAction.isChecked()) return { _arcSinFactorAction.getValue() }; - return { _arcSinFactorsAction.getValues() }; - + return _arcSinFactorsAction.getValues(); } // TODO: add gui to optionally set per-channel cofactor @@ -197,13 +206,15 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Conversion& type) { - const auto createGroupAction = [this](WidgetAction& widgetAction) -> GroupAction* { + const auto createGroupAction = [this]() -> GroupAction* { auto groupAction = new GroupAction(this, "PointDataConversionGroupAction"); groupAction->setText("Settings"); groupAction->setToolTip("Data conversion settings"); groupAction->setLabelSizingType(GroupAction::LabelSizingType::Auto); - groupAction->addAction(&widgetAction); + groupAction->addAction(&_sameFactorAction); + groupAction->addAction(&_arcSinFactorAction); + groupAction->addAction(&_arcSinFactorsAction); // TODO: not correctly enabled on first open return groupAction; }; @@ -214,7 +225,7 @@ WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const Poi return nullptr; case PointDataConversionPlugin::Conversion::ArcSin: - return createGroupAction(_arcSinFactorsAction); + return createGroupAction(); } return nullptr; diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index b020bb3..bca1214 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -70,8 +71,8 @@ class PointDataConversionPlugin : public mv::plugin::TransformationPlugin static QString getConversionName(const Conversion& conversion); private: - Conversion _conversion; /** Data conversion type */ - std::vector _cofactors; + Conversion _conversion = Conversion::ArcSin; + std::vector _cofactors = { 5.f }; }; /** @@ -121,7 +122,7 @@ class PointDataConversionPluginFactory : public mv::plugin::TransformationPlugin std::vector getArcSinCoFactor() const; private: - bool _sameCofactor = true; - mv::gui::DecimalAction _arcSinFactorAction; /** Factor for arcsin(value/factor) conversion */ - mv::gui::SlidersAction _arcSinFactorsAction; /** Factor for arcsin(value/factor) conversion */ + mv::gui::ToggleAction _sameFactorAction; + mv::gui::DecimalAction _arcSinFactorAction; + mv::gui::SlidersAction _arcSinFactorsAction; }; From c40e6cfdbb49f3a565bb9a3043dc0886d4667f34 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Tue, 4 Aug 2026 17:59:09 +0200 Subject: [PATCH 16/20] Fix first disable --- src/PointDataConversionPlugin.cpp | 2 +- src/SlidersAction.cpp | 7 ++++++- src/SlidersAction.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index 2fce78f..28ec521 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -214,7 +214,7 @@ WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const Poi groupAction->setLabelSizingType(GroupAction::LabelSizingType::Auto); groupAction->addAction(&_sameFactorAction); groupAction->addAction(&_arcSinFactorAction); - groupAction->addAction(&_arcSinFactorsAction); // TODO: not correctly enabled on first open + groupAction->addAction(&_arcSinFactorsAction); return groupAction; }; diff --git a/src/SlidersAction.cpp b/src/SlidersAction.cpp index f0e48e7..ad8b2ec 100644 --- a/src/SlidersAction.cpp +++ b/src/SlidersAction.cpp @@ -11,7 +11,7 @@ SlidersAction::SlidersAction(QObject* parent, const QString& title) : WidgetAction(parent, title) { setText(title); - setDefaultWidgetFlags(SlidersAction::Default); + setDefaultWidgetFlags(SlidersAction::DisableOnFirstOpen); } void SlidersAction::initialize(const QStringList& options) { @@ -99,6 +99,8 @@ QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFla auto* layout = new QVBoxLayout(container); layout->setContentsMargins(4, 4, 4, 4); + const bool disableAll = _sliderList == nullptr && widgetFlags == WidgetFlag::DisableOnFirstOpen; + _sliderList = new QListWidget(container); _sliderList->setSelectionMode(QAbstractItemView::NoSelection); layout->addWidget(_sliderList); @@ -125,5 +127,8 @@ QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFla }); } + if (disableAll) + setAllSlidersEnabled(false); + return container; } diff --git a/src/SlidersAction.h b/src/SlidersAction.h index bae21c2..76e0c67 100644 --- a/src/SlidersAction.h +++ b/src/SlidersAction.h @@ -20,6 +20,7 @@ namespace mv::gui { /** Describes the widget flags */ enum WidgetFlag { Default = 0x00001, + DisableOnFirstOpen = 0x00002, }; public: From f5590e700b225b574c3154956977f9c8be4bec8f Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 5 Aug 2026 09:41:10 +0200 Subject: [PATCH 17/20] Refactor: Option to Entry --- src/PointDataConversionPlugin.cpp | 19 ++++---- src/PointDataConversionPlugin.h | 2 +- src/SlidersAction.cpp | 72 +++++++++++++++++-------------- src/SlidersAction.h | 21 ++++----- 4 files changed, 61 insertions(+), 53 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index 28ec521..a289c60 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -112,21 +112,15 @@ QString PointDataConversionPlugin::getConversionName(const Conversion& conversio PointDataConversionPluginFactory::PointDataConversionPluginFactory() : _sameFactorAction(this, "Same factor", true), _arcSinFactorAction(this, "Factor", - SlidersAction::OptionData::DEFAULT_MIN, SlidersAction::OptionData::DEFAULT_MAX, - SlidersAction::OptionData::DEFAULT_VALUE, SlidersAction::OptionData::DEFAULT_DECIMALS), + SlidersAction::EntryData::DEFAULT_MIN, SlidersAction::EntryData::DEFAULT_MAX, + SlidersAction::EntryData::DEFAULT_VALUE, SlidersAction::EntryData::DEFAULT_DECIMALS), _arcSinFactorsAction(this, "Factors") { - QStringList opts = { "1", "2", "3" }; - _arcSinFactorsAction.setOptions(opts); - for (const auto& opt : opts) - _arcSinFactorsAction.setDataForOption(opt, 5.f, 0.f, 100.f); - connect(&_sameFactorAction, &ToggleAction::toggled, this, [&](bool toggled) { _arcSinFactorAction.setEnabled(_sameFactorAction.isChecked()); _arcSinFactorsAction.setAllSlidersEnabled(!_sameFactorAction.isChecked()); }); - } PointDataConversionPlugin* PointDataConversionPluginFactory::produce() @@ -204,9 +198,14 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c return pluginTriggerActions; } -WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Conversion& type) +WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset) { - const auto createGroupAction = [this]() -> GroupAction* { + const auto createGroupAction = [this, &inputDataset]() -> GroupAction* { + + const std::vector dimNamesVec = mv::Dataset(inputDataset)->getDimensionNames(); + const QStringList dimNamesList(dimNamesVec.begin(), dimNamesVec.end()); + _arcSinFactorsAction.initialize(dimNamesList); + auto groupAction = new GroupAction(this, "PointDataConversionGroupAction"); groupAction->setText("Settings"); diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index bca1214..535fb50 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -116,7 +116,7 @@ class PointDataConversionPluginFactory : public mv::plugin::TransformationPlugin * Get configuration action for \p type * @return Pointer to configuration action (may be null) */ - WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Conversion& type); + WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset); private: std::vector getArcSinCoFactor() const; diff --git a/src/SlidersAction.cpp b/src/SlidersAction.cpp index ad8b2ec..2b87ab4 100644 --- a/src/SlidersAction.cpp +++ b/src/SlidersAction.cpp @@ -14,65 +14,73 @@ SlidersAction::SlidersAction(QObject* parent, const QString& title) : setDefaultWidgetFlags(SlidersAction::DisableOnFirstOpen); } -void SlidersAction::initialize(const QStringList& options) { - setOptions(options); +void SlidersAction::initialize(const QStringList& entries) { + setEntries(entries); + setAllEntriesToDefault(); } -void SlidersAction::setOptions(const QStringList& options) { - _options = options; - // populate default OptionData if new - for (const QString& opt : options) { - if (!_optionData.contains(opt)) { - _optionData.insert({ opt, OptionData{} }); +void SlidersAction::setEntries(const QStringList& entries) { + _entries = entries; + // populate default EntryData if new + for (const QString& opt : entries) { + if (!_entryData.contains(opt)) { + _entryData.insert({ opt, EntryData{} }); } } - // remove data for removed options - for (auto it = _optionData.begin(); it != _optionData.end(); ) { - if (!options.contains(it->first)) - it = _optionData.erase(it); + // remove data for removed entries + for (auto it = _entryData.begin(); it != _entryData.end(); ) { + if (!entries.contains(it->first)) + it = _entryData.erase(it); else ++it; } } -void SlidersAction::setRangeForOption(const QString& option, float minimum, float maximum) +void SlidersAction::setAllEntriesToDefault() { - if (!_optionData.contains(option)) return; - auto& d = _optionData[option]; + for (const auto& [name, data] : _entryData) + setDataForEntry(name, 5.f, 0.f, 100.f); + +} + +void SlidersAction::setRangeForEntry(const QString& entry, float minimum, float maximum) +{ + if (!_entryData.contains(entry)) return; + auto& d = _entryData[entry]; d.min = minimum; d.max = maximum; d.value = std::clamp(d.value, d.min, d.max); } -void SlidersAction::setValueForOption(const QString& option, float value) +void SlidersAction::setValueForEntry(const QString& entry, float value) { - if (!_optionData.contains(option)) return; - auto& d = _optionData[option]; + if (!_entryData.contains(entry)) return; + auto& d = _entryData[entry]; value = std::clamp(value, d.min, d.max); if (std::abs(d.value - value) < 0.0001f) return; d.value = value; - emit optionValueChanged(option, value); + emit entryValueChanged(entry, value); } -void SlidersAction::setDataForOption(const QString& option, float value, float minimum, float maximum) +void SlidersAction::setDataForEntry(const QString& entry, float value, float minimum, float maximum) { - setRangeForOption(option, minimum, maximum); - setValueForOption(option, value); + setRangeForEntry(entry, minimum, maximum); + setValueForEntry(entry, value); } -float SlidersAction::getValueForOption(const QString& option) const +float SlidersAction::getValueForEntry(const QString& entry) const { - if (!_optionData.contains(option)) + if (!_entryData.contains(entry)) return 0.0f; - return _optionData.at(option).value; + return _entryData.at(entry).value; } std::vector SlidersAction::getValues() const { std::vector values; - values.reserve(_options.size()); + values.reserve(_entries.size()); - for (const auto& opt : _options) - values.push_back(getValueForOption(opt)); + for (const auto& opt : _entries) + values.push_back(getValueForEntry(opt)); return values; } @@ -105,16 +113,16 @@ QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFla _sliderList->setSelectionMode(QAbstractItemView::NoSelection); layout->addWidget(_sliderList); - for (const QString& opt : _options) { + for (const QString& opt : _entries) { auto* item = new QListWidgetItem(_sliderList); QWidget* row = new QWidget(_sliderList); QHBoxLayout* rowLayout = new QHBoxLayout(row); rowLayout->setContentsMargins(2, 2, 2, 2); - const OptionData& d = _optionData.at(opt); + const EntryData& d = _entryData.at(opt); - DecimalAction* slider = new DecimalAction(this, opt, d.min, d.max, d.value, OptionData::DEFAULT_DECIMALS); + DecimalAction* slider = new DecimalAction(this, opt, d.min, d.max, d.value, EntryData::DEFAULT_DECIMALS); rowLayout->addWidget(slider->createLabelWidget(container)); rowLayout->addWidget(slider->createWidget(container)); @@ -123,7 +131,7 @@ QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFla item->setSizeHint(row->sizeHint()); connect(slider, &DecimalAction::valueChanged, this, [this, opt](float value) { - setValueForOption(opt, value); + setValueForEntry(opt, value); }); } diff --git a/src/SlidersAction.h b/src/SlidersAction.h index 76e0c67..9b61561 100644 --- a/src/SlidersAction.h +++ b/src/SlidersAction.h @@ -24,7 +24,7 @@ namespace mv::gui { }; public: - struct OptionData { + struct EntryData { static constexpr float DEFAULT_MIN = 1.0f; static constexpr float DEFAULT_MAX = 100.0f; static constexpr float DEFAULT_VALUE = 5.0f; @@ -38,26 +38,27 @@ namespace mv::gui { public: explicit SlidersAction(QObject* parent, const QString& title); - void initialize(const QStringList& options = QStringList()); - void setOptions(const QStringList& options); + void initialize(const QStringList& entries = QStringList()); + void setEntries(const QStringList& entries); + void setAllEntriesToDefault(); - void setRangeForOption(const QString& option, float minimum, float maximum); - void setValueForOption(const QString& option, float value); - void setDataForOption(const QString& option, float value, float minimum, float maximum); - [[nodiscard]] float getValueForOption(const QString& option) const; + void setRangeForEntry(const QString& entry, float minimum, float maximum); + void setValueForEntry(const QString& entry, float value); + void setDataForEntry(const QString& entry, float value, float minimum, float maximum); + [[nodiscard]] float getValueForEntry(const QString& entry) const; [[nodiscard]] std::vector getValues() const; void setAllSlidersEnabled(bool enabled); signals: - void optionValueChanged(const QString& option, float value); + void entryValueChanged(const QString& entry, float value); protected: QWidget* getWidget(QWidget* parent, const std::int32_t& widgetFlags) override; private: - QStringList _options = {}; - std::unordered_map _optionData = {}; + QStringList _entries = {}; + std::unordered_map _entryData = {}; QListWidget* _sliderList = nullptr; }; } From 8b7f0ff0ab22e06c0c21ff5edf4ba99e218659d5 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 5 Aug 2026 11:50:55 +0200 Subject: [PATCH 18/20] Add cofactor settings UI to plugin --- src/PointDataConversionPlugin.cpp | 93 ++++++++++++++++++++++++------- src/PointDataConversionPlugin.h | 35 +++++++++++- 2 files changed, 106 insertions(+), 22 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index a289c60..abc0b48 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -136,7 +136,6 @@ std::vector PointDataConversionPluginFactory::getArcSinCoFactor() const return _arcSinFactorsAction.getValues(); } -// TODO: add gui to optionally set per-channel cofactor PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(const mv::Datasets& datasets) const { PluginTriggerActions pluginTriggerActions; @@ -147,11 +146,7 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, QString("Conversion/%1").arg(typeName), QString("Perform %1 data conversion").arg(typeName), icon(), [this, datasets, type](PluginTriggerAction& pluginTriggerAction) -> void { for (const auto& dataset : datasets) { - auto pluginInstance = dynamic_cast(plugins().requestPlugin(getKind())); - - pluginInstance->setInputDataset(dataset); - pluginInstance->setConversion(type); - pluginInstance->transform(); + const_cast(this)->openConfigDialog(type, dataset); } }); @@ -165,7 +160,7 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c return pluginTriggerActions; } -// This is used in the image viewer +// This is used in e.g. the image viewer PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(const mv::DataTypes& dataTypes) const { PluginTriggerActions pluginTriggerActions; @@ -176,13 +171,7 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c auto pluginTriggerAction = new PluginTriggerAction(const_cast(this), this, QString("Conversion/%1").arg(typeName), QString("Perform %1 data conversion").arg(typeName), icon(), [this, type](PluginTriggerAction& pluginTriggerAction) -> void { for (const auto& dataset : pluginTriggerAction.getDatasets()) { - auto pluginInstance = dynamic_cast(plugins().requestPlugin(getKind())); - - pluginInstance->setInputDataset(dataset); - pluginInstance->setConversion(type); - pluginInstance->setCofactor(getArcSinCoFactor()); - - pluginInstance->transform(); + createPluginAndTransform(type, dataset); } }); @@ -198,22 +187,19 @@ PluginTriggerActions PointDataConversionPluginFactory::getPluginTriggerActions(c return pluginTriggerActions; } -WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset) +WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const PointDataConversionPlugin::Conversion& type) { - const auto createGroupAction = [this, &inputDataset]() -> GroupAction* { + const auto createGroupAction = [this]() -> GroupAction* { - const std::vector dimNamesVec = mv::Dataset(inputDataset)->getDimensionNames(); - const QStringList dimNamesList(dimNamesVec.begin(), dimNamesVec.end()); - _arcSinFactorsAction.initialize(dimNamesList); + _arcSinFactorsAction.initialize({}); + _sameFactorAction.setChecked(true); auto groupAction = new GroupAction(this, "PointDataConversionGroupAction"); groupAction->setText("Settings"); groupAction->setToolTip("Data conversion settings"); groupAction->setLabelSizingType(GroupAction::LabelSizingType::Auto); - groupAction->addAction(&_sameFactorAction); groupAction->addAction(&_arcSinFactorAction); - groupAction->addAction(&_arcSinFactorsAction); return groupAction; }; @@ -229,3 +215,68 @@ WidgetAction* PointDataConversionPluginFactory::getConfigurationAction(const Poi return nullptr; } + + +void PointDataConversionPluginFactory::openConfigDialog(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset) +{ + _sameFactorAction.setChecked(true); + const std::vector dimNamesVec = mv::Dataset(inputDataset)->getDimensionNames(); + const QStringList dimNamesList(dimNamesVec.begin(), dimNamesVec.end()); + _arcSinFactorsAction.initialize(dimNamesList); + + switch (type) + { + case PointDataConversionPlugin::Conversion::Log2: + createPluginAndTransform(type, inputDataset); + break; + + case PointDataConversionPlugin::Conversion::ArcSin: + { + ConversionDialog inputDialog(nullptr, &_sameFactorAction, &_arcSinFactorAction, &_arcSinFactorsAction); + inputDialog.setModal(true); + if (inputDialog.exec() == QDialog::Accepted) + createPluginAndTransform(type, inputDataset); + + break; + } + } + +} + +void PointDataConversionPluginFactory::createPluginAndTransform(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset) const +{ + auto pluginInstance = dynamic_cast(plugins().requestPlugin(getKind())); + + pluginInstance->setInputDataset(inputDataset); + pluginInstance->setConversion(type); + pluginInstance->setCofactor(getArcSinCoFactor()); + pluginInstance->transform(); + +} + +// ============================================================================= +// Helper +// ============================================================================= + +ConversionDialog::ConversionDialog(QWidget* parent, ToggleAction* sameFactorAction, DecimalAction* arcSinFactorAction, SlidersAction* arcSinFactorsAction) : + QDialog(parent), _conversionButton(this, "Convert") +{ + setWindowTitle(tr("Data conversion settings")); + + connect(&_conversionButton, &TriggerAction::triggered, this, &ConversionDialog::closeDialogAction); + + auto* layout = new QHBoxLayout(); + + auto groupAction = new GroupAction(this, "PointDataConversionGroupAction"); + + groupAction->setText("Settings"); + groupAction->setToolTip("Data conversion settings"); + groupAction->setLabelSizingType(GroupAction::LabelSizingType::Auto); + groupAction->addAction(sameFactorAction); + groupAction->addAction(arcSinFactorAction); + groupAction->addAction(arcSinFactorsAction); + groupAction->addAction(&_conversionButton); + + layout->addWidget(groupAction->createWidget(this)); + setLayout(layout); +} diff --git a/src/PointDataConversionPlugin.h b/src/PointDataConversionPlugin.h index 535fb50..6d15b31 100644 --- a/src/PointDataConversionPlugin.h +++ b/src/PointDataConversionPlugin.h @@ -116,7 +116,17 @@ class PointDataConversionPluginFactory : public mv::plugin::TransformationPlugin * Get configuration action for \p type * @return Pointer to configuration action (may be null) */ - WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset); + WidgetAction* getConfigurationAction(const PointDataConversionPlugin::Conversion& type); + + /** + * Show option dialog and run transform + */ + void openConfigDialog(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset); + + /** + * Create a transformation plugin and apply transformation + */ + void createPluginAndTransform(const PointDataConversionPlugin::Conversion& type, const mv::Dataset& inputDataset) const; private: std::vector getArcSinCoFactor() const; @@ -126,3 +136,26 @@ class PointDataConversionPluginFactory : public mv::plugin::TransformationPlugin mv::gui::DecimalAction _arcSinFactorAction; mv::gui::SlidersAction _arcSinFactorsAction; }; + +/** + * Helper dialog to set conversion options + * + * @author Alex Vieth + */ +class ConversionDialog : public QDialog +{ + Q_OBJECT +public: + explicit ConversionDialog(QWidget* parent, mv::gui::ToggleAction* sameFactorAction, mv::gui::DecimalAction* arcSinFactorAction, mv::gui::SlidersAction* arcSinFactorsAction); + +signals: + void closeDialog(bool onlyIndices); + +private slots: + void closeDialogAction() { + emit QDialog::accept(); + } + +private: + mv::gui::TriggerAction _conversionButton; +}; From 533356ac0bfdf79d1f71fd614396095df58e6553 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 5 Aug 2026 11:57:44 +0200 Subject: [PATCH 19/20] actually use dimension specific co-factors --- src/PointDataConversionPlugin.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index abc0b48..bf830be 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -46,12 +47,17 @@ void PointDataConversionPlugin::transform() points->visitData([this, &points, &task](auto pointData) { std::atomic_uint64_t noPointsProcessed = 0; - float cofactor = _cofactors[0]; const auto numPointsF = static_cast(points->getNumPoints()); const auto numPointsI = static_cast(points->getNumPoints()); const auto numDims = points->getNumDimensions(); - qDebug() << "PointDataConversionPlugin::transform: cofactor of" << cofactor ; + assert(!_cofactors.empty()); + assert(_cofactors.size() == 1 || _cofactors.size() == numDims); + + if (_cofactors.size() == 1) + qDebug() << "PointDataConversionPlugin::transform: cofactor of" << _cofactors[0]; + else + qDebug() << "PointDataConversionPlugin::transform: cofactors of" << _cofactors; #pragma omp parallel for for (std::int64_t pointIndex = 0; pointIndex < numPointsI; pointIndex++) { @@ -64,6 +70,9 @@ void PointDataConversionPlugin::transform() break; case Conversion::ArcSin: + + const float cofactor = (_cofactors.size() == 1) ? _cofactors[0] : _cofactors[dimensionIndex]; + pointData[pointIndex][dimensionIndex] = std::asinhf(pointData[pointIndex][dimensionIndex] / cofactor); break; } From 0d00834ea87ad4169de89c6808b8a3b518079129 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 5 Aug 2026 13:28:04 +0200 Subject: [PATCH 20/20] Couple single factor to multiple --- src/PointDataConversionPlugin.cpp | 33 ++++++++++++++++++++++++------- src/SlidersAction.cpp | 26 +++++++++++++++++++++++- src/SlidersAction.h | 3 +++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/PointDataConversionPlugin.cpp b/src/PointDataConversionPlugin.cpp index bf830be..b255ceb 100644 --- a/src/PointDataConversionPlugin.cpp +++ b/src/PointDataConversionPlugin.cpp @@ -120,16 +120,35 @@ QString PointDataConversionPlugin::getConversionName(const Conversion& conversio PointDataConversionPluginFactory::PointDataConversionPluginFactory() : _sameFactorAction(this, "Same factor", true), - _arcSinFactorAction(this, "Factor", - SlidersAction::EntryData::DEFAULT_MIN, SlidersAction::EntryData::DEFAULT_MAX, - SlidersAction::EntryData::DEFAULT_VALUE, SlidersAction::EntryData::DEFAULT_DECIMALS), + _arcSinFactorAction(this, "Factor"), _arcSinFactorsAction(this, "Factors") { + _arcSinFactorAction.setToolTip("Apply the same cofactors to each channel."); + _arcSinFactorsAction.setToolTip("Apply different cofactors to each channel."); + + _arcSinFactorAction.initialize(SlidersAction::EntryData::DEFAULT_MIN, SlidersAction::EntryData::DEFAULT_MAX, + SlidersAction::EntryData::DEFAULT_VALUE, SlidersAction::EntryData::DEFAULT_DECIMALS); + connect(&_sameFactorAction, &ToggleAction::toggled, this, [&](bool toggled) - { - _arcSinFactorAction.setEnabled(_sameFactorAction.isChecked()); - _arcSinFactorsAction.setAllSlidersEnabled(!_sameFactorAction.isChecked()); - }); + { + _arcSinFactorAction.setEnabled(_sameFactorAction.isChecked()); + _arcSinFactorsAction.setAllSlidersEnabled(!_sameFactorAction.isChecked()); + }); + + connect(&_arcSinFactorAction, &DecimalAction::valueChanged, this, [&](float value) + { + const auto sliderValues = _arcSinFactorsAction.getValues(); + + const bool allEqual = !sliderValues.empty() && + std::all_of(sliderValues.cbegin() + 1, sliderValues.cend(), + [&](const float v) { return std::abs(v - sliderValues.front()) < 0.0001f; }); + + if (!allEqual) + return; + + _arcSinFactorsAction.setValueForAllEntries(value); + }); + } PointDataConversionPlugin* PointDataConversionPluginFactory::produce() diff --git a/src/SlidersAction.cpp b/src/SlidersAction.cpp index 2b87ab4..12a96ce 100644 --- a/src/SlidersAction.cpp +++ b/src/SlidersAction.cpp @@ -51,6 +51,14 @@ void SlidersAction::setRangeForEntry(const QString& entry, float minimum, float d.value = std::clamp(d.value, d.min, d.max); } +void SlidersAction::setValueForAllEntries(float value) +{ + for (const auto& [name, data] : _entryData) + setValueForEntry(name, value); + + setAllSlidersValues(value); +} + void SlidersAction::setValueForEntry(const QString& entry, float value) { if (!_entryData.contains(entry)) return; @@ -101,6 +109,22 @@ void SlidersAction::setAllSlidersEnabled(bool enabled) } +void SlidersAction::setAllSlidersValues(float value) +{ + if (!_sliderList) + return; + + for (int i = 0; i < _sliderList->count(); ++i) { + QListWidgetItem* item = _sliderList->item(i); + if (QWidget* row = _sliderList->itemWidget(item)) { + if (auto* slider = row->findChild()) { + slider->setValue(value); + } + } + } + +} + QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFlags) { auto* container = new QWidget(parent); @@ -122,7 +146,7 @@ QWidget* SlidersAction::getWidget(QWidget* parent, const std::int32_t& widgetFla const EntryData& d = _entryData.at(opt); - DecimalAction* slider = new DecimalAction(this, opt, d.min, d.max, d.value, EntryData::DEFAULT_DECIMALS); + DecimalAction* slider = new DecimalAction(row, opt, d.min, d.max, d.value, EntryData::DEFAULT_DECIMALS); rowLayout->addWidget(slider->createLabelWidget(container)); rowLayout->addWidget(slider->createWidget(container)); diff --git a/src/SlidersAction.h b/src/SlidersAction.h index 9b61561..5f9ea4a 100644 --- a/src/SlidersAction.h +++ b/src/SlidersAction.h @@ -44,11 +44,13 @@ namespace mv::gui { void setRangeForEntry(const QString& entry, float minimum, float maximum); void setValueForEntry(const QString& entry, float value); + void setValueForAllEntries(float value); // also updates sliders void setDataForEntry(const QString& entry, float value, float minimum, float maximum); [[nodiscard]] float getValueForEntry(const QString& entry) const; [[nodiscard]] std::vector getValues() const; void setAllSlidersEnabled(bool enabled); + void setAllSlidersValues(float value); signals: void entryValueChanged(const QString& entry, float value); @@ -60,5 +62,6 @@ namespace mv::gui { QStringList _entries = {}; std::unordered_map _entryData = {}; QListWidget* _sliderList = nullptr; + }; }