From f40c0350b567a721bb7810573f0f90688bea6683 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 1 Aug 2025 12:36:48 +0200 Subject: [PATCH 1/6] Reset view earlier --- src/ScatterplotPlugin.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 3cf788a..e452312 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -853,14 +853,18 @@ void ScatterplotPlugin::fromVariantMap(const QVariantMap& variantMap) variantMapMustContain(variantMap, "Settings"); + auto& pointRenderer = const_cast(_scatterPlotWidget->getPointRenderer()); + + pointRenderer.getNavigator().resetView(true); + _primaryToolbarAction.fromParentVariantMap(variantMap); _settingsAction.fromParentVariantMap(variantMap); - auto& pointRenderer = const_cast(_scatterPlotWidget->getPointRenderer()); + if (pointRenderer.getNavigator().getNavigationAction().getSerializationCountFrom() == 0) { qDebug() << "Resetting view"; - pointRenderer.getNavigator().resetView(true); + _scatterPlotWidget->update(); } From e7cffe753d589c49897e56bd046623c2bef64698 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Thu, 14 Aug 2025 15:16:52 +0200 Subject: [PATCH 2/6] Recolor with HSNE embedding (#185) * WIP: use global indices if number of points does not match * Allow recoloring of hsne embeddings * Set point size and opacity same as color Remove double call to addColorDataset * Only allow resizing points for now (no size or opacity) * Restore indendation --- src/ColoringAction.cpp | 3 ++ src/PointPlotAction.cpp | 18 ++++++++++ src/PointPlotAction.h | 12 +++++++ src/ScalarAction.cpp | 3 ++ src/ScalarSourceModel.cpp | 9 ++++- src/ScalarSourceModel.h | 7 ++++ src/ScatterplotPlugin.cpp | 70 ++++++++++++++++++++++++++++----------- 7 files changed, 102 insertions(+), 20 deletions(-) diff --git a/src/ColoringAction.cpp b/src/ColoringAction.cpp index 0ade6d0..4c26333 100644 --- a/src/ColoringAction.cpp +++ b/src/ColoringAction.cpp @@ -179,6 +179,9 @@ Dataset ColoringAction::getCurrentColorDataset() const void ColoringAction::setCurrentColorDataset(const Dataset& colorDataset) { + if (!colorDataset.isValid()) + return; + addColorDataset(colorDataset); const auto colorDatasetRowIndex = _colorByModel.rowIndex(colorDataset); diff --git a/src/PointPlotAction.cpp b/src/PointPlotAction.cpp index 287ef99..e5c33ab 100644 --- a/src/PointPlotAction.cpp +++ b/src/PointPlotAction.cpp @@ -210,6 +210,24 @@ void PointPlotAction::addPointOpacityDataset(const Dataset& pointOp _opacityAction.addDataset(pointOpacityDataset); } +void PointPlotAction::setCurrentPointSizeDataset(const Dataset& pointSizeDataset) +{ + if (!pointSizeDataset.isValid()) + return; + + addPointSizeDataset(pointSizeDataset); + _sizeAction.setCurrentDataset(pointSizeDataset); +} + +void PointPlotAction::setCurrentPointOpacityDataset(const Dataset& pointOpacityDataset) +{ + if (!pointOpacityDataset.isValid()) + return; + + addPointOpacityDataset(pointOpacityDataset); + _opacityAction.setCurrentDataset(pointOpacityDataset); +} + void PointPlotAction::updateDefaultDatasets() { if (_scatterplotPlugin == nullptr) diff --git a/src/PointPlotAction.h b/src/PointPlotAction.h index 41f050a..00db5c4 100644 --- a/src/PointPlotAction.h +++ b/src/PointPlotAction.h @@ -58,6 +58,18 @@ class PointPlotAction : public VerticalGroupAction */ void addPointOpacityDataset(const Dataset& pointOpacityDataset); + /** + * Set the current point size dataset + * @param pointSizeDataset Smart pointer to point size dataset + */ + void setCurrentPointSizeDataset(const Dataset& pointSizeDataset); + + /** + * Set the current opacity size dataset + * @param pointOpacityDataset Smart pointer to opacity size dataset + */ + void setCurrentPointOpacityDataset(const Dataset& pointOpacityDataset); + protected: /** Update default datasets (candidates are children of points type and with matching number of points) */ diff --git a/src/ScalarAction.cpp b/src/ScalarAction.cpp index 747a25a..0c8ef8d 100644 --- a/src/ScalarAction.cpp +++ b/src/ScalarAction.cpp @@ -38,6 +38,9 @@ void ScalarAction::addDataset(const Dataset& dataset) { auto& sourceModel = _sourceAction.getModel(); + if (sourceModel.hasDataset(dataset)) + return; + sourceModel.addDataset(dataset); connect(&sourceModel.getDatasets().last(), &Dataset::dataChanged, this, [this, dataset]() { diff --git a/src/ScalarSourceModel.cpp b/src/ScalarSourceModel.cpp index db68e58..d3220ce 100644 --- a/src/ScalarSourceModel.cpp +++ b/src/ScalarSourceModel.cpp @@ -74,6 +74,8 @@ QVariant ScalarSourceModel::data(const QModelIndex& index, int role) const if (row == DefaultRow::Selection) return "Selection"; } + + break; } default: @@ -86,7 +88,7 @@ QVariant ScalarSourceModel::data(const QModelIndex& index, int role) const void ScalarSourceModel::addDataset(const Dataset& dataset) { // Avoid duplicates - if (rowIndex(dataset) >= DefaultRow::DatasetStart) + if (hasDataset(dataset)) return; // Insert row into model @@ -127,6 +129,11 @@ void ScalarSourceModel::addDataset(const Dataset& dataset) }); } +bool ScalarSourceModel::hasDataset(const Dataset& dataset) const +{ + return rowIndex(dataset) >= DefaultRow::DatasetStart; +} + void ScalarSourceModel::removeDataset(const Dataset& dataset) { // Get row index of the dataset diff --git a/src/ScalarSourceModel.h b/src/ScalarSourceModel.h index f9ca5b8..9fd86e7 100644 --- a/src/ScalarSourceModel.h +++ b/src/ScalarSourceModel.h @@ -65,6 +65,13 @@ class ScalarSourceModel : public QAbstractListModel */ void addDataset(const Dataset& dataset); + /** + * Determines whether a given dataset is already loaded + * @param dataset Smart pointer to dataset + * @return whether the dataset is already loaded + */ + bool hasDataset(const Dataset& dataset) const; + /** * Remove a dataset * @param dataset Smart pointer to dataset diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index e452312..6a3136e 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -18,6 +18,7 @@ #include #include +#include #include @@ -29,9 +30,8 @@ #include #include -#include +#include #include -#include #define VIEW_SAMPLING_HTML //#define VIEW_SAMPLING_WIDGET @@ -172,24 +172,33 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : }); } - if (candidateDataset->getNumPoints() == _positionDataset->getNumPoints()) { - - // The number of points is equal, so offer the option to use the points dataset as source for points colors + // Accept both data with the same number if points and data which is derived from + // a parent that has the same number of points (e.g. for HSNE embeddings) + const auto numPointsCandidate = candidateDataset->getNumPoints(); + const auto numPointsPosition = _positionDataset->getNumPoints(); + const bool sameNumPoints = numPointsPosition == numPointsCandidate; + const bool sameNumPointsAsFull = + /*if*/ _positionDataset->isDerivedData() ? + /*then*/ _positionDataset->getSourceDataset()->getFullDataset()->getNumPoints() == numPointsCandidate : + /*else*/ false; + + if (sameNumPoints || sameNumPointsAsFull) { + // Offer the option to use the points dataset as source for points colors dropRegions << new DropWidget::DropRegion(this, "Point color", QString("Colorize %1 points with %2").arg(_positionDataset->text(), candidateDataset->text()), "palette", true, [this, candidateDataset]() { - _settingsAction.getColoringAction().addColorDataset(candidateDataset); - _settingsAction.getColoringAction().setCurrentColorDataset(candidateDataset); + _settingsAction.getColoringAction().setCurrentColorDataset(candidateDataset); // calls addColorDataset internally }); - // The number of points is equal, so offer the option to use the points dataset as source for points size + } + + if (sameNumPoints) { + // Offer the option to use the points dataset as source for points size dropRegions << new DropWidget::DropRegion(this, "Point size", QString("Size %1 points with %2").arg(_positionDataset->text(), candidateDataset->text()), "ruler-horizontal", true, [this, candidateDataset]() { - _settingsAction.getPlotAction().getPointPlotAction().addPointSizeDataset(candidateDataset); - _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().setCurrentDataset(candidateDataset); + _settingsAction.getPlotAction().getPointPlotAction().setCurrentPointSizeDataset(candidateDataset); }); - // The number of points is equal, so offer the option to use the points dataset as source for points opacity + // Offer the option to use the points dataset as source for points opacity dropRegions << new DropWidget::DropRegion(this, "Point opacity", QString("Set %1 points opacity with %2").arg(_positionDataset->text(), candidateDataset->text()), "brush", true, [this, candidateDataset]() { - _settingsAction.getPlotAction().getPointPlotAction().addPointOpacityDataset(candidateDataset); - _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().setCurrentDataset(candidateDataset); + _settingsAction.getPlotAction().getPointPlotAction().setCurrentPointOpacityDataset(candidateDataset); }); } } @@ -647,14 +656,37 @@ void ScatterplotPlugin::loadColors(const Dataset& points, const std::uin // Generate point scalars for color mapping std::vector scalars; - if (_positionDataset->getNumPoints() != _numPoints) - { - qWarning("Number of points used for coloring does not match number of points in data, aborting attempt to color plot"); - return; + points->extractDataForDimension(scalars, dimensionIndex); + + const auto numColorPoints = points->getNumPoints(); + + + if (numColorPoints != _numPoints) { + + const bool sameNumPointsAsFull = + /*if*/ _positionDataset->isDerivedData() ? + /*then*/ _positionSourceDataset->getFullDataset()->getNumPoints() == numColorPoints : + /*else*/ false; + + if (sameNumPointsAsFull) { + std::vector globalIndices; + _positionDataset->getGlobalIndices(globalIndices); + + std::vector localScalars(_numPoints, 0); + std::int32_t localColorIndex = 0; + + for (const auto& globalIndex : globalIndices) + localScalars[localColorIndex++] = scalars[globalIndex]; + + std::swap(localScalars, scalars); + } + else { + qWarning("Number of points used for coloring does not match number of points in data, aborting attempt to color plot"); + return; + } } - // Populate point scalars - points->extractDataForDimension(scalars, dimensionIndex); + assert(scalars.size() == _numPoints); // Assign scalars and scalar effect _scatterPlotWidget->setScalars(scalars); From 6114835c183b214e8754c8aa4f5498eb560e395a Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+basusoumyadeep@users.noreply.github.com> Date: Thu, 21 Aug 2025 20:13:40 +0200 Subject: [PATCH 3/6] Add dimension changed event to coloring point dataset --- src/ColoringAction.cpp | 17 +++++++++++++++-- src/ColoringAction.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ColoringAction.cpp b/src/ColoringAction.cpp index 0ade6d0..2419a6f 100644 --- a/src/ColoringAction.cpp +++ b/src/ColoringAction.cpp @@ -71,11 +71,24 @@ ColoringAction::ColoringAction(QObject* parent, const QString& title) : _constantColorAction.setEnabled(currentIndex == 0); const auto currentColorDataset = getCurrentColorDataset(); - + disconnect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, nullptr); + _currentColorPointsDataset = Dataset(); if (currentColorDataset.isValid()) { const auto currentColorDatasetTypeIsPointType = currentColorDataset->getDataType() == PointType; - _dimensionAction.setPointsDataset(currentColorDatasetTypeIsPointType ? Dataset(currentColorDataset) : Dataset()); + if (currentColorDatasetTypeIsPointType) { + _currentColorPointsDataset = currentColorDataset.get(); + connect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, [this]() { + _dimensionAction.setPointsDataset(_currentColorPointsDataset); + updateScatterPlotWidgetColors(); + }); + _dimensionAction.setPointsDataset(_currentColorPointsDataset); + + + } + else { + _dimensionAction.setPointsDataset(Dataset()); + } //_dimensionAction.setVisible(currentColorDatasetTypeIsPointType); emit currentColorDatasetChanged(currentColorDataset); diff --git a/src/ColoringAction.h b/src/ColoringAction.h index 2cc75b2..cf9dc87 100644 --- a/src/ColoringAction.h +++ b/src/ColoringAction.h @@ -134,6 +134,7 @@ class ColoringAction : public VerticalGroupAction DimensionPickerAction _dimensionAction; /** Dimension picker action */ ColorMap1DAction _colorMap1DAction; /** One-dimensional color map action */ ColorMap2DAction _colorMap2DAction; /** Two-dimensional color map action */ + Dataset _currentColorPointsDataset; /** Current color dataset */ /** Default constant color */ static const QColor DEFAULT_CONSTANT_COLOR; From 124064c0500d09d31bfbf50045601f47198c769a Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+basusoumyadeep@users.noreply.github.com> Date: Thu, 21 Aug 2025 20:40:20 +0200 Subject: [PATCH 4/6] add dataset validity checks --- src/ColoringAction.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ColoringAction.cpp b/src/ColoringAction.cpp index 2419a6f..611be0d 100644 --- a/src/ColoringAction.cpp +++ b/src/ColoringAction.cpp @@ -71,20 +71,23 @@ ColoringAction::ColoringAction(QObject* parent, const QString& title) : _constantColorAction.setEnabled(currentIndex == 0); const auto currentColorDataset = getCurrentColorDataset(); - disconnect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, nullptr); + if (_currentColorPointsDataset.isValid()) + { + disconnect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, nullptr); + } _currentColorPointsDataset = Dataset(); if (currentColorDataset.isValid()) { const auto currentColorDatasetTypeIsPointType = currentColorDataset->getDataType() == PointType; - if (currentColorDatasetTypeIsPointType) { + if (currentColorDatasetTypeIsPointType) { _currentColorPointsDataset = currentColorDataset.get(); - connect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, [this]() { - _dimensionAction.setPointsDataset(_currentColorPointsDataset); - updateScatterPlotWidgetColors(); - }); + if (_currentColorPointsDataset.isValid()) { + connect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, [this]() { + _dimensionAction.setPointsDataset(_currentColorPointsDataset); + updateScatterPlotWidgetColors(); + }); + } _dimensionAction.setPointsDataset(_currentColorPointsDataset); - - } else { _dimensionAction.setPointsDataset(Dataset()); From 618cb7e8da77e267959909aade1668d74a81f53b Mon Sep 17 00:00:00 2001 From: Soumyadeep Basu <44787782+basusoumyadeep@users.noreply.github.com> Date: Thu, 21 Aug 2025 20:49:58 +0200 Subject: [PATCH 5/6] add dataset validity check inside connect --- src/ColoringAction.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ColoringAction.cpp b/src/ColoringAction.cpp index 611be0d..2458392 100644 --- a/src/ColoringAction.cpp +++ b/src/ColoringAction.cpp @@ -83,11 +83,18 @@ ColoringAction::ColoringAction(QObject* parent, const QString& title) : _currentColorPointsDataset = currentColorDataset.get(); if (_currentColorPointsDataset.isValid()) { connect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, [this]() { - _dimensionAction.setPointsDataset(_currentColorPointsDataset); - updateScatterPlotWidgetColors(); + if (_currentColorPointsDataset.isValid()) + { + _dimensionAction.setPointsDataset(_currentColorPointsDataset); + updateScatterPlotWidgetColors(); + } }); + _dimensionAction.setPointsDataset(_currentColorPointsDataset); } - _dimensionAction.setPointsDataset(_currentColorPointsDataset); + else { + _dimensionAction.setPointsDataset(Dataset()); + } + } else { _dimensionAction.setPointsDataset(Dataset()); From 5512ee440c694b89fc71ebeb48e0be784621989f Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 22 Aug 2025 08:03:56 +0200 Subject: [PATCH 6/6] Coding/style improvements --- src/ColoringAction.cpp | 18 +++++++++--------- src/ColoringAction.h | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ColoringAction.cpp b/src/ColoringAction.cpp index 2458392..6463937 100644 --- a/src/ColoringAction.cpp +++ b/src/ColoringAction.cpp @@ -71,30 +71,30 @@ ColoringAction::ColoringAction(QObject* parent, const QString& title) : _constantColorAction.setEnabled(currentIndex == 0); const auto currentColorDataset = getCurrentColorDataset(); - if (_currentColorPointsDataset.isValid()) - { + + if (_currentColorPointsDataset.isValid()) { disconnect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, nullptr); } + _currentColorPointsDataset = Dataset(); - if (currentColorDataset.isValid()) { - const auto currentColorDatasetTypeIsPointType = currentColorDataset->getDataType() == PointType; - if (currentColorDatasetTypeIsPointType) { + if (currentColorDataset.isValid()) { + if (currentColorDataset->getDataType() == PointType) { _currentColorPointsDataset = currentColorDataset.get(); + if (_currentColorPointsDataset.isValid()) { connect(&_currentColorPointsDataset, &Dataset::dataDimensionsChanged, this, [this]() { - if (_currentColorPointsDataset.isValid()) - { + if (_currentColorPointsDataset.isValid()) { _dimensionAction.setPointsDataset(_currentColorPointsDataset); updateScatterPlotWidgetColors(); } - }); + }); + _dimensionAction.setPointsDataset(_currentColorPointsDataset); } else { _dimensionAction.setPointsDataset(Dataset()); } - } else { _dimensionAction.setPointsDataset(Dataset()); diff --git a/src/ColoringAction.h b/src/ColoringAction.h index cf9dc87..4a1d480 100644 --- a/src/ColoringAction.h +++ b/src/ColoringAction.h @@ -127,14 +127,14 @@ class ColoringAction : public VerticalGroupAction void currentColorDatasetChanged(Dataset currentColorDataset); private: - ScatterplotPlugin* _scatterplotPlugin; /** Pointer to scatter plot plugin */ - ColorSourceModel _colorByModel; /** Color by model (model input for the color by action) */ - OptionAction _colorByAction; /** Action for picking the coloring type */ - ColorAction _constantColorAction; /** Action for picking the constant color */ - DimensionPickerAction _dimensionAction; /** Dimension picker action */ - ColorMap1DAction _colorMap1DAction; /** One-dimensional color map action */ - ColorMap2DAction _colorMap2DAction; /** Two-dimensional color map action */ - Dataset _currentColorPointsDataset; /** Current color dataset */ + ScatterplotPlugin* _scatterplotPlugin; /** Pointer to scatter plot plugin */ + ColorSourceModel _colorByModel; /** Color by model (model input for the color by action) */ + OptionAction _colorByAction; /** Action for picking the coloring type */ + ColorAction _constantColorAction; /** Action for picking the constant color */ + DimensionPickerAction _dimensionAction; /** Dimension picker action */ + ColorMap1DAction _colorMap1DAction; /** One-dimensional color map action */ + ColorMap2DAction _colorMap2DAction; /** Two-dimensional color map action */ + Dataset _currentColorPointsDataset; /** Current color dataset */ /** Default constant color */ static const QColor DEFAULT_CONSTANT_COLOR;