From f945cb58299739161778752221f882fed4b43916 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Wed, 13 Aug 2025 17:05:37 +0200 Subject: [PATCH 1/5] WIP: use global indices if number of points does not match --- src/ScatterplotPlugin.cpp | 46 +++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index e452312..a9cf58d 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,7 +172,17 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : }); } - if (candidateDataset->getNumPoints() == _positionDataset->getNumPoints()) { + //const auto numPointsCand = candidateDataset->getNumPoints(); + //const auto numPointsPos = _positionDataset->getNumPoints(); + //const bool sameNumPoints = numPointsCand == numPointsPos; + //bool sameNumPointsAsFull = false; + + //if (_positionDataset->isDerivedData()) { + // const auto numPointsSor = _positionDataset->getSourceDataset()->getNumPoints(); + // sameNumPointsAsFull = numPointsCand == numPointsSor; + //} + + //if (sameNumPoints || sameNumPointsAsFull) { // The number of points is equal, so 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]() { @@ -191,7 +201,7 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : _settingsAction.getPlotAction().getPointPlotAction().addPointOpacityDataset(candidateDataset); _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().setCurrentDataset(candidateDataset); }); - } + //} } } @@ -647,15 +657,29 @@ 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; - } - - // Populate point scalars points->extractDataForDimension(scalars, dimensionIndex); + //if (_positionSourceDataset->getNumPoints() == points->getNumPoints()) + //{ + 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 if (points->getNumPoints() != _numPoints) { + // qWarning("Number of points used for coloring does not match number of points in data, aborting attempt to color plot"); + // return; + //} + + assert(scalars.size() == _numPoints); + // Assign scalars and scalar effect _scatterPlotWidget->setScalars(scalars); _scatterPlotWidget->setScalarEffect(PointEffect::Color); From 797c7e8666d59ffd23f4582764cc36ca689966ad Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Thu, 14 Aug 2025 13:43:47 +0200 Subject: [PATCH 2/5] Allow recoloring of hsne embeddings --- src/ScatterplotPlugin.cpp | 67 ++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index a9cf58d..354775a 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -172,17 +172,17 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : }); } - //const auto numPointsCand = candidateDataset->getNumPoints(); - //const auto numPointsPos = _positionDataset->getNumPoints(); - //const bool sameNumPoints = numPointsCand == numPointsPos; - //bool sameNumPointsAsFull = false; - - //if (_positionDataset->isDerivedData()) { - // const auto numPointsSor = _positionDataset->getSourceDataset()->getNumPoints(); - // sameNumPointsAsFull = numPointsCand == numPointsSor; - //} - - //if (sameNumPoints || sameNumPointsAsFull) { + // 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) { // The number of points is equal, so 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]() { @@ -201,7 +201,7 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : _settingsAction.getPlotAction().getPointPlotAction().addPointOpacityDataset(candidateDataset); _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().setCurrentDataset(candidateDataset); }); - //} + } } } @@ -580,9 +580,9 @@ void ScatterplotPlugin::samplePoints() if (getSamplerAction().getRestrictNumberOfElementsAction().isChecked() && numberOfPoints >= getSamplerAction().getMaximumNumberOfElementsAction().getValue()) break; - const auto& distance = sampledPoint.first; - const auto& localPointIndex = sampledPoint.second; - const auto& globalPointIndex = localGlobalIndices[localPointIndex]; + const auto& distance = sampledPoint.first; + const auto& localPointIndex = sampledPoint.second; + const auto& globalPointIndex = localGlobalIndices[localPointIndex]; distances << distance; localPointIndices << localPointIndex; @@ -659,24 +659,33 @@ void ScatterplotPlugin::loadColors(const Dataset& points, const std::uin points->extractDataForDimension(scalars, dimensionIndex); - //if (_positionSourceDataset->getNumPoints() == points->getNumPoints()) - //{ - std::vector globalIndices; - _positionDataset->getGlobalIndices(globalIndices); + const auto numColorPoints = points->getNumPoints(); - std::vector localScalars(_numPoints, 0); - std::int32_t localColorIndex = 0; - for (const auto& globalIndex : globalIndices) - localScalars[localColorIndex++] = scalars[globalIndex]; + if (numColorPoints != _numPoints) { + + const bool sameNumPointsAsFull = + /*if*/ _positionDataset->isDerivedData() ? + /*then*/ _positionSourceDataset->getFullDataset()->getNumPoints() == numColorPoints : + /*else*/ false; - std::swap(localScalars, scalars); + if (sameNumPointsAsFull) { + std::vector globalIndices; + _positionDataset->getGlobalIndices(globalIndices); - //} - //else if (points->getNumPoints() != _numPoints) { - // qWarning("Number of points used for coloring does not match number of points in data, aborting attempt to color plot"); - // return; - //} + 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; + } + } assert(scalars.size() == _numPoints); From 8653ca8c3fc28102085575bb3d57346a01bab78b Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Thu, 14 Aug 2025 14:06:48 +0200 Subject: [PATCH 3/5] Set point size and opacity same as color Remove double call to addColorDataset --- 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 | 15 ++++++--------- 7 files changed, 57 insertions(+), 10 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 354775a..ebc55ea 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -184,22 +184,19 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : if (sameNumPoints || sameNumPointsAsFull) { - // The number of points is equal, so offer the option to use the points dataset as source for points colors + // 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 + // 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); }); } } From 4a4c00b1e9cd7e900365f89df90736e8c41b9cd8 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Thu, 14 Aug 2025 14:17:07 +0200 Subject: [PATCH 4/5] Only allow resizing points for now (no size or opacity) --- src/ScatterplotPlugin.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index ebc55ea..1ffc2b9 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -183,12 +183,14 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : /*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().setCurrentColorDataset(candidateDataset); // calls addColorDataset internally }); + } + + 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().setCurrentPointSizeDataset(candidateDataset); From fdcb4656588b12d75e7898dbdb5f626d5285f542 Mon Sep 17 00:00:00 2001 From: Alexander Vieth Date: Thu, 14 Aug 2025 14:27:46 +0200 Subject: [PATCH 5/5] Restore indendation --- src/ScatterplotPlugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 1ffc2b9..6a3136e 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -579,9 +579,9 @@ void ScatterplotPlugin::samplePoints() if (getSamplerAction().getRestrictNumberOfElementsAction().isChecked() && numberOfPoints >= getSamplerAction().getMaximumNumberOfElementsAction().getValue()) break; - const auto& distance = sampledPoint.first; - const auto& localPointIndex = sampledPoint.second; - const auto& globalPointIndex = localGlobalIndices[localPointIndex]; + const auto& distance = sampledPoint.first; + const auto& localPointIndex = sampledPoint.second; + const auto& globalPointIndex = localGlobalIndices[localPointIndex]; distances << distance; localPointIndices << localPointIndex;