From de79c84dd6f23400c5c488817c619c3157d7ca41 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 17 Oct 2025 11:15:33 +0200 Subject: [PATCH 01/10] Add condition for Sentry upload on release branches [skip ci] --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67472a6..61ed56b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,7 @@ jobs: conan-password: ${{secrets.LKEB_UPLOAD_USER_PASSWORD}} conan-pem: ${{secrets.LKEB_UPLOAD_CERT_CHAIN}} rs_ssh_key: ${{ secrets.RULESSUPPORT_DEPLOY_KEY }} + sentry-upload: startsWith(github.ref, 'refs/heads/release/') sentry-url: ${{secrets.LKEB_SENTRY_URL }} sentry-org: ${{secrets.LKEB_SENTRY_ORG }} sentry-project: ${{secrets.LKEB_SENTRY_PROJECT }} From 0ca8f81d0f18a120aa884cd5691a20f61cc745e3 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 24 Oct 2025 12:27:44 +0200 Subject: [PATCH 02/10] Update HUD text color based on background Added updateHeadsUpDisplayTextColor to adjust the Heads Up Display text color for better contrast depending on the background color. The text color now updates automatically when the background color changes or when loading settings. --- src/ScatterplotPlugin.cpp | 21 +++++++++++++++++++-- src/ScatterplotPlugin.h | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 8a6610d..0429892 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -264,8 +264,6 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : getLearningCenterAction().addVideos(QStringList({ "Practitioner", "Developer" })); setOverlayActionsTargetWidget(_scatterPlotWidget); - - } ScatterplotPlugin::~ScatterplotPlugin() @@ -376,6 +374,10 @@ void ScatterplotPlugin::init() connect(&_settingsAction.getColoringAction().getColorByAction(), &OptionAction::currentIndexChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); connect(&_settingsAction.getPlotAction().getPointPlotAction().getSizeAction(), &ScalarAction::sourceDataChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); connect(&_settingsAction.getPlotAction().getPointPlotAction().getOpacityAction(), &ScalarAction::sourceDataChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + + updateHeadsUpDisplayTextColor(); + + connect(&_settingsAction.getMiscellaneousAction().getBackgroundColorAction(), &ColorAction::colorChanged, this, &ScatterplotPlugin::updateHeadsUpDisplayTextColor); } void ScatterplotPlugin::loadData(const Datasets& datasets) @@ -993,6 +995,19 @@ void ScatterplotPlugin::updateHeadsUpDisplay() } } +void ScatterplotPlugin::updateHeadsUpDisplayTextColor() +{ + if (auto headsUpDisplayWidget = getWidget().findChild("HeadsUpDisplayWidget")) { + if (auto headsUpDisplayWidgetTreeView = headsUpDisplayWidget->findChild("TreeView")) { + QPalette palette = headsUpDisplayWidgetTreeView->palette(); + + palette.setColor(QPalette::Text, _settingsAction.getMiscellaneousAction().getBackgroundColorAction().getColor().lightnessF() > .5f ? Qt::black : Qt::white); + + headsUpDisplayWidgetTreeView->setPalette(palette); + } + } +} + void ScatterplotPlugin::fromVariantMap(const QVariantMap& variantMap) { ViewPlugin::fromVariantMap(variantMap); @@ -1014,6 +1029,8 @@ void ScatterplotPlugin::fromVariantMap(const QVariantMap& variantMap) _scatterPlotWidget->update(); } + + updateHeadsUpDisplayTextColor(); } QVariantMap ScatterplotPlugin::toVariantMap() const diff --git a/src/ScatterplotPlugin.h b/src/ScatterplotPlugin.h index 4ef3c3f..e795c72 100644 --- a/src/ScatterplotPlugin.h +++ b/src/ScatterplotPlugin.h @@ -95,6 +95,7 @@ class ScatterplotPlugin : public ViewPlugin void updateSelection(); void updateHeadsUpDisplay(); + void updateHeadsUpDisplayTextColor(); public: // Serialization From 741494d3c21dec2c61b871ee6950995f6e7018d0 Mon Sep 17 00:00:00 2001 From: JulianThijssen Date: Mon, 27 Oct 2025 14:18:40 +0100 Subject: [PATCH 03/10] Add check to stop being able to drop whatever cluster data on scatterplot data (#215) * Add a check when dropping cluster data on a scatterplot to check whether its compatible * Don't copy clusters when tallying --- src/ScatterplotPlugin.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 0429892..3056c2e 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -238,12 +238,30 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : }); } else { - - // Use the clusters set for points color - dropRegions << new DropWidget::DropRegion(this, "Color", description, "palette", true, [this, candidateDataset]() { - _settingsAction.getColoringAction().addColorDataset(candidateDataset); - _settingsAction.getColoringAction().setCurrentColorDataset(candidateDataset); - }); + if (candidateDataset.isValid()) + { + // Check to set whether the number of data points comprised throughout all clusters is the same number + // as the number of data points in the dataset we are trying to color + int totalNumIndices = 0; + for (const Cluster& cluster : candidateDataset->getClusters()) + { + totalNumIndices += cluster.getIndices().size(); + } + + if (totalNumIndices == _positionDataset->getNumPoints()) + { + // Use the clusters set for points color + dropRegions << new DropWidget::DropRegion(this, "Color", description, "palette", true, [this, candidateDataset]() { + _settingsAction.getColoringAction().addColorDataset(candidateDataset); + _settingsAction.getColoringAction().setCurrentColorDataset(candidateDataset); + }); + } + else + { + // Number of indices in clusters doesn't match point dataset + dropRegions << new DropWidget::DropRegion(this, "Incompatible data", "Cluster data does not match number of data points", "exclamation-circle", false); + } + } } } else { From b682e6b37dac6269acd934ba75d0caa68eedecea Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 27 Oct 2025 14:57:30 +0100 Subject: [PATCH 04/10] Formatting: Move variable to where it's used --- src/ScatterplotPlugin.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 0429892..1555562 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -797,9 +797,6 @@ void ScatterplotPlugin::loadColors(const Dataset& clusters) if (!clusters.isValid() || !_positionDataset.isValid()) return; - // Mapping from local to global indices - std::vector globalIndices; - // Get global indices from the position dataset int totalNumPoints = 0; if (_positionDataset->isDerivedData()) @@ -807,6 +804,8 @@ void ScatterplotPlugin::loadColors(const Dataset& clusters) else totalNumPoints = _positionDataset->getFullDataset()->getNumPoints(); + // Mapping from local to global indices + std::vector globalIndices; _positionDataset->getGlobalIndices(globalIndices); // Generate color buffer for global and local colors From 99c7c03b4a2c35870cd10ddd6247afe7ec90e161 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 27 Oct 2025 14:58:05 +0100 Subject: [PATCH 05/10] Formatting: Use member variable instead of manual check --- src/ScatterplotPlugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 1555562..cc95657 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -810,11 +810,11 @@ void ScatterplotPlugin::loadColors(const Dataset& clusters) // Generate color buffer for global and local colors std::vector globalColors(totalNumPoints); - std::vector localColors(_positions.size()); + std::vector localColors(_numPoints); const auto& clusterVec = clusters->getClusters(); - if (totalNumPoints == _positions.size() && clusterVec.size() == totalNumPoints) + if (totalNumPoints == _numPoints && clusterVec.size() == totalNumPoints) { for (size_t i = 0; i < static_cast(clusterVec.size()); i++) { From de2ad83b1c1d1cb8e0a3791256f4724b77f9835b Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 27 Oct 2025 14:58:30 +0100 Subject: [PATCH 06/10] Clean-up: Do not repeat construction of same color --- src/ScatterplotPlugin.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index cc95657..ce9e67d 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -830,9 +830,10 @@ void ScatterplotPlugin::loadColors(const Dataset& clusters) // Loop over all clusters and populate global colors for (const auto& cluster : clusterVec) { - const auto color = cluster.getColor(); + const auto color = cluster.getColor(); + const auto colVec = Vector3f(color.redF(), color.greenF(), color.blueF()); for (const auto& index : cluster.getIndices()) - globalColors[index] = Vector3f(color.redF(), color.greenF(), color.blueF()); + globalColors[index] = colVec; } From f59ab58464dd8fc96ee27c0b4f9bc24c3b1a7a70 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 27 Oct 2025 14:58:48 +0100 Subject: [PATCH 07/10] Fix crash on cluster and point mismatch --- src/ScatterplotPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index ce9e67d..05ddec0 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -825,7 +825,7 @@ void ScatterplotPlugin::loadColors(const Dataset& clusters) } } - else + else if(globalIndices.size() == _numPoints) { // Loop over all clusters and populate global colors for (const auto& cluster : clusterVec) From e75d7c60ab0efcfb70f7a3bf2e1d4bf1df16e2f5 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 27 Oct 2025 14:59:12 +0100 Subject: [PATCH 08/10] Respect cluster re-coloring on startup --- src/ScatterplotWidget.cpp | 6 ++++-- src/ScatterplotWidget.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ScatterplotWidget.cpp b/src/ScatterplotWidget.cpp index 535c985..ff6f6f8 100644 --- a/src/ScatterplotWidget.cpp +++ b/src/ScatterplotWidget.cpp @@ -41,6 +41,7 @@ ScatterplotWidget::ScatterplotWidget(mv::plugin::ViewPlugin* parentPlugin) : _pointRenderer(this), _isInitialized(false), _renderMode(SCATTERPLOT), + _scalarEffect(PointEffect::Color), _backgroundColor(255, 255, 255, 255), _coloringMode(ColoringMode::Constant), _dataRectangleAction(this, "Data rectangle"), @@ -334,7 +335,7 @@ void ScatterplotWidget::setScalars(const std::vector& scalars) void ScatterplotWidget::setColors(const std::vector& colors) { _pointRenderer.setColors(colors); - _pointRenderer.setScalarEffect(None); + setScalarEffect(PointEffect::None); update(); } @@ -367,6 +368,7 @@ void ScatterplotWidget::setPointScaling(mv::gui::PointScaling scalingMode) void ScatterplotWidget::setScalarEffect(PointEffect effect) { _pointRenderer.setScalarEffect(effect); + _scalarEffect = effect; update(); } @@ -621,7 +623,7 @@ void ScatterplotWidget::initializeGL() _densityRenderer.init(); // Set a default color map for both renderers - _pointRenderer.setScalarEffect(PointEffect::Color); + _pointRenderer.setScalarEffect(_scalarEffect); _pointRenderer.setPointScaling(Absolute); _pointRenderer.setSelectionOutlineColor(Vector3f(1, 0, 0)); diff --git a/src/ScatterplotWidget.h b/src/ScatterplotWidget.h index cf0618a..9e73b49 100644 --- a/src/ScatterplotWidget.h +++ b/src/ScatterplotWidget.h @@ -296,6 +296,7 @@ private slots: private: bool _isInitialized; /** Boolean determining whether the widget it properly initialized or not */ RenderMode _renderMode; /** Current render mode */ + PointEffect _scalarEffect; /** Current scalar effect */ QColor _backgroundColor; /** Background color */ ColoringMode _coloringMode; /** Type of point/density coloring */ DecimalRectangleAction _dataRectangleAction; /** Rectangle action for the bounds of the loaded data */ From 9e35f148e718155f2eb3e5b8e60da9e98d5b847e Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 27 Oct 2025 14:59:24 +0100 Subject: [PATCH 09/10] Clean-up: less duplication --- src/ColoringAction.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/ColoringAction.cpp b/src/ColoringAction.cpp index 18e04e8..7c52fed 100644 --- a/src/ColoringAction.cpp +++ b/src/ColoringAction.cpp @@ -127,24 +127,29 @@ ColoringAction::ColoringAction(QObject* parent, const QString& title) : connect(&_scatterplotPlugin->getPositionDataset(), &Dataset::childAdded, this, &ColoringAction::updateColorByActionOptions); connect(&_scatterplotPlugin->getPositionDataset(), &Dataset::childRemoved, this, &ColoringAction::updateColorByActionOptions); - connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::renderModeChanged, this, &ColoringAction::updateScatterPlotWidgetColors); - connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::coloringModeChanged, this, &ColoringAction::updateScatterPlotWidgetColors); + connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::coloringModeChanged, this, [this](const ScatterplotWidget::ColoringMode& coloringMode) { + updateScatterPlotWidgetColors(); + updateColorMapActionsReadOnly(); + }); + + connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::renderModeChanged, this, [this](const ScatterplotWidget::RenderMode& renderMode) { + updateScatterPlotWidgetColors(); + updateColorMapActionsReadOnly(); + }); - connect(&_dimensionAction, &DimensionPickerAction::currentDimensionIndexChanged, this, &ColoringAction::updateScatterPlotWidgetColors); - connect(&_dimensionAction, &DimensionPickerAction::currentDimensionIndexChanged, this, &ColoringAction::updateColorMapActionScalarRange); + connect(&_dimensionAction, &DimensionPickerAction::currentDimensionIndexChanged, this, [this](const int32_t& currentDimensionIndex) { + updateScatterPlotWidgetColors(); + updateColorMapActionsReadOnly(); + updateColorMapActionScalarRange(); + }); connect(&_constantColorAction, &ColorAction::colorChanged, this, &ColoringAction::updateScatterplotWidgetColorMap); connect(&_colorMap1DAction, &ColorMapAction::imageChanged, this, &ColoringAction::updateScatterplotWidgetColorMap); connect(&_colorMap2DAction, &ColorMapAction::imageChanged, this, &ColoringAction::updateScatterplotWidgetColorMap); - connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::coloringModeChanged, this, &ColoringAction::updateScatterplotWidgetColorMap); - connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::renderModeChanged, this, &ColoringAction::updateScatterplotWidgetColorMap); connect(&_colorMap1DAction.getRangeAction(ColorMapAction::Axis::X), &DecimalRangeAction::rangeChanged, this, &ColoringAction::updateScatterPlotWidgetColorMapRange); connect(&_colorMap2DAction.getRangeAction(ColorMapAction::Axis::X), &DecimalRangeAction::rangeChanged, this, &ColoringAction::updateScatterPlotWidgetColorMapRange); - connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::coloringModeChanged, this, &ColoringAction::updateColorMapActionsReadOnly); - connect(&_scatterplotPlugin->getScatterplotWidget(), &ScatterplotWidget::renderModeChanged, this, &ColoringAction::updateColorMapActionsReadOnly); - const auto updateReadOnly = [this]() { setEnabled(_scatterplotPlugin->getPositionDataset().isValid() && _scatterplotPlugin->getScatterplotWidget().getRenderMode() == ScatterplotWidget::SCATTERPLOT); }; From b402206320ec49c11140922dbf4e5326e0a91609 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Mon, 27 Oct 2025 14:59:44 +0100 Subject: [PATCH 10/10] Clean-up: use local variable instead of repeated function call --- src/ColoringAction.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ColoringAction.cpp b/src/ColoringAction.cpp index 7c52fed..c3fca1f 100644 --- a/src/ColoringAction.cpp +++ b/src/ColoringAction.cpp @@ -281,7 +281,8 @@ void ColoringAction::updateScatterplotWidgetColorMap() { case ScatterplotWidget::SCATTERPLOT: { - if (_colorByAction.getCurrentIndex() == 0) { + const int32_t currentIndex = _colorByAction.getCurrentIndex(); + if (currentIndex == 0) { QPixmap colorPixmap(1, 1); colorPixmap.fill(_constantColorAction.getColor()); @@ -290,7 +291,7 @@ void ColoringAction::updateScatterplotWidgetColorMap() scatterplotWidget.setScalarEffect(PointEffect::Color); scatterplotWidget.setColoringMode(ScatterplotWidget::ColoringMode::Constant); } - else if (_colorByAction.getCurrentIndex() == 1) { + else if (currentIndex == 1) { scatterplotWidget.setColorMap(_colorMap2DAction.getColorMapImage()); scatterplotWidget.setScalarEffect(PointEffect::Color2D); scatterplotWidget.setColoringMode(ScatterplotWidget::ColoringMode::Scatter);