Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
28e7706
Fix warnings and update points (#240)
alxvth Mar 30, 2026
109e3d1
Update core requirement due to previous commit
JulianThijssen Apr 17, 2026
a555b33
Adhere to new serialization API (#243)
ThomasKroes Jun 30, 2026
09914e2
Set current point dataset when opacity dataset changed
ThomasKroes Jul 21, 2026
004ae93
Add extra null guard
ThomasKroes Jul 21, 2026
4b62d3a
Merge pull request #245 from ManiVaultStudio/feature/fix_opacity_sele…
sbvis Jul 21, 2026
9810b4c
Extends coloring options for scatterplot (addressing issue #24) (#247)
thoellt Jul 23, 2026
5104caf
Fixes Qt 6.10 build
thoellt Jul 24, 2026
7485c00
CI: Remove Release build and install steps (#248)
alxvth Jul 24, 2026
a86c3e0
Upgrade build workflow actions and Python version
alxvth Jul 24, 2026
73bf4db
Revert principal dimension action name change (#250)
ThomasKroes Jul 28, 2026
a3db8a6
Remove restrictive condition
alxvth Aug 17, 2026
aac4630
Use range for, eliminates index
alxvth Aug 17, 2026
3dc0df5
Track totalPoints class wide
alxvth Aug 17, 2026
0a17314
Check if cluster indices exceed point indices instead of checking of …
alxvth Aug 17, 2026
7c4e5a8
Revert last 4 commits
alxvth Aug 17, 2026
81b78ea
Accept more valid clusters for coloring (#253)
alxvth Aug 18, 2026
29f99aa
Merge branch 'release/core_bican_bg/bican_bg' into master
ThomasKroes Aug 18, 2026
fd6db0c
Add Z-order controls and dimension-based selection restrictions (#254)
ThomasKroes Sep 10, 2026
a458c7b
Merge branch 'release/core_bican_bg/bican_bg' into master
ThomasKroes Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,14 @@ set(Actions
src/ScalarSourceAction.cpp
src/SelectionAction.h
src/SelectionAction.cpp
src/SelectionRestrictionAction.h
src/SelectionRestrictionAction.cpp
src/SettingsAction.h
src/SettingsAction.cpp
src/SubsetAction.h
src/SubsetAction.cpp
src/ZOrderingAction.h
src/ZOrderingAction.cpp
src/ExportAction.h
src/ExportAction.cpp
src/DatasetsAction.h
Expand Down
17 changes: 2 additions & 15 deletions src/MiscellaneousAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ const QColor MiscellaneousAction::DEFAULT_BACKGROUND_COLOR = qRgb(255, 255, 255)
MiscellaneousAction::MiscellaneousAction(QObject* parent, const QString& title) :
VerticalGroupAction(parent, title),
_scatterplotPlugin(dynamic_cast<ScatterplotPlugin*>(parent->parent())),
_backgroundColorAction(this, "Background color"),
_randomizedDepthAction(this, "Randomized depth", true)
_backgroundColorAction(this, "Background color")
{
setIconByName("cog");
setLabelSizingType(LabelSizingType::Auto);
setConfigurationFlag(WidgetAction::ConfigurationFlag::ForceCollapsedInGroup);

addAction(&_backgroundColorAction);
addAction(&_randomizedDepthAction);

_backgroundColorAction.setColor(DEFAULT_BACKGROUND_COLOR);

Expand All @@ -32,15 +30,6 @@ MiscellaneousAction::MiscellaneousAction(QObject* parent, const QString& title)

updateBackgroundColor();

const auto updateRandomizedDepth = [this]() -> void {
_scatterplotPlugin->getScatterplotWidget().setRandomizedDepthEnabled(_randomizedDepthAction.isChecked());
};

connect(&_randomizedDepthAction, &ToggleAction::toggled, this, [this, updateRandomizedDepth](bool toggled) {
updateRandomizedDepth();
});

updateRandomizedDepth();
}

QMenu* MiscellaneousAction::getContextMenu()
Expand Down Expand Up @@ -85,15 +74,13 @@ void MiscellaneousAction::fromVariantMap(const QVariantMap& variantMap)
GroupAction::fromVariantMap(variantMap);

_backgroundColorAction.fromParentVariantMap(variantMap);
_randomizedDepthAction.fromParentVariantMap(variantMap);
}

QVariantMap MiscellaneousAction::toVariantMap() const
{
auto variantMap = GroupAction::toVariantMap();

_backgroundColorAction.insertIntoVariantMap(variantMap);
_randomizedDepthAction.insertIntoVariantMap(variantMap);

return variantMap;
}
}
5 changes: 1 addition & 4 deletions src/MiscellaneousAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include <actions/VerticalGroupAction.h>
#include <actions/ColorAction.h>
#include <actions/ToggleAction.h>

using namespace mv::gui;

Expand Down Expand Up @@ -66,12 +65,10 @@ class MiscellaneousAction : public VerticalGroupAction
public: // Action getters

ColorAction& getBackgroundColorAction() { return _backgroundColorAction; }
ToggleAction& getRandomizedDepthAction() { return _randomizedDepthAction; }

private:
ScatterplotPlugin* _scatterplotPlugin; /** Pointer to scatter plot plugin */
ColorAction _backgroundColorAction; /** Color action for setting the background color action */
ToggleAction _randomizedDepthAction; /** whether the z-order of each point is to be randomized or not */

static const QColor DEFAULT_BACKGROUND_COLOR;

Expand All @@ -80,4 +77,4 @@ class MiscellaneousAction : public VerticalGroupAction

Q_DECLARE_METATYPE(MiscellaneousAction)

inline const auto miscellaneousActionMetaTypeId = qRegisterMetaType<MiscellaneousAction*>("MiscellaneousAction");
inline const auto miscellaneousActionMetaTypeId = qRegisterMetaType<MiscellaneousAction*>("MiscellaneousAction");
103 changes: 100 additions & 3 deletions src/ScatterplotPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) :
_primaryToolbarAction->addAction(&_settingsAction->getDatasetsAction());
_primaryToolbarAction->addAction(&_settingsAction->getRenderModeAction(), 3, GroupAction::Horizontal);
_primaryToolbarAction->addAction(&_settingsAction->getPositionAction(), 1, GroupAction::Horizontal);
_primaryToolbarAction->addAction(&_settingsAction->getZOrderingAction(), 1, GroupAction::Horizontal);
_primaryToolbarAction->addAction(&_settingsAction->getPlotAction(), 2, GroupAction::Horizontal);
_primaryToolbarAction->addAction(&_settingsAction->getColoringAction());
_primaryToolbarAction->addAction(&_settingsAction->getSubsetAction());
Expand Down Expand Up @@ -499,6 +500,78 @@ void ScatterplotPlugin::createSubset(const bool& fromSourceData /*= false*/, con
subset->getDataHierarchyItem().select();
}

void ScatterplotPlugin::selectAllEligiblePoints()
{
if (!_positionDataset.isValid())
return;

std::vector<std::uint32_t> globalIndices;
_positionDataset->getGlobalIndices(globalIndices);

std::vector<std::uint32_t> eligibleIndices;
eligibleIndices.reserve(globalIndices.size());

for (std::uint32_t localIndex = 0; localIndex < globalIndices.size(); ++localIndex) {
if (!_scatterPlotWidget->isSelectionExcluded(localIndex))
eligibleIndices.push_back(globalIndices[localIndex]);
}

_positionDataset->setSelectionIndices(eligibleIndices);
events().notifyDatasetDataSelectionChanged(_positionDataset->getSourceDataset<Points>());
}

void ScatterplotPlugin::invertEligiblePointSelection()
{
if (!_positionDataset.isValid())
return;

const auto selection = _positionDataset->getSelection<Points>();

std::vector<bool> selected;
_positionDataset->selectedLocalIndices(selection->indices, selected);

std::vector<std::uint32_t> globalIndices;
_positionDataset->getGlobalIndices(globalIndices);

std::vector<std::uint32_t> invertedIndices;
invertedIndices.reserve(globalIndices.size());

for (std::uint32_t localIndex = 0; localIndex < globalIndices.size(); ++localIndex) {
if (!_scatterPlotWidget->isSelectionExcluded(localIndex) && (localIndex >= selected.size() || !selected[localIndex]))
invertedIndices.push_back(globalIndices[localIndex]);
}

_positionDataset->setSelectionIndices(invertedIndices);
events().notifyDatasetDataSelectionChanged(_positionDataset->getSourceDataset<Points>());
}

void ScatterplotPlugin::filterSelectionExcludedIndices(std::vector<std::uint32_t>& globalIndices) const
{
if (!_positionDataset.isValid()) {
globalIndices.clear();
return;
}

std::vector<bool> selected;
_positionDataset->selectedLocalIndices(globalIndices, selected);

std::vector<std::uint32_t> localGlobalIndices;
_positionDataset->getGlobalIndices(localGlobalIndices);

globalIndices.clear();
globalIndices.reserve(localGlobalIndices.size());

for (std::uint32_t localIndex = 0; localIndex < localGlobalIndices.size(); ++localIndex) {
if (localIndex < selected.size() && selected[localIndex] && !_scatterPlotWidget->isSelectionExcluded(localIndex))
globalIndices.push_back(localGlobalIndices[localIndex]);
}
}

void ScatterplotPlugin::refreshSelection()
{
updateSelection();
}

void ScatterplotPlugin::selectPoints()
{
if (getSettingsAction().getSelectionAction().getFreezeSelectionAction().isChecked())
Expand Down Expand Up @@ -536,6 +609,9 @@ void ScatterplotPlugin::selectPoints()

// Go over all points in the dataset to see if they are selected
for (std::uint32_t localPointIndex = 0; localPointIndex < _positions.size(); localPointIndex++) {
if (_scatterPlotWidget->isSelectionExcluded(localPointIndex))
continue;

const auto& point = _positions[localPointIndex];

// Compute the offset of the point in the world space
Expand Down Expand Up @@ -610,6 +686,8 @@ void ScatterplotPlugin::selectPoints()
}
}

filterSelectionExcludedIndices(targetSelectionIndices);

auto& navigationAction = navigator.getNavigationAction();

navigationAction.getZoomSelectionAction().setEnabled(!targetSelectionIndices.empty() && navigationAction.isNavigationActive());
Expand Down Expand Up @@ -650,6 +728,9 @@ void ScatterplotPlugin::samplePoints()
// Go over all points in the dataset to see if they should be sampled
for (std::uint32_t localPointIndex = 0; localPointIndex < _positions.size(); localPointIndex++) {

if (_scatterPlotWidget->isSelectionExcluded(localPointIndex))
continue;

// Compute the offset of the point in the world space
const auto pointOffsetWorld = QPointF(_positions[localPointIndex].x - zoomRectangleWorld.left(), _positions[localPointIndex].y - zoomRectangleWorld.top());

Expand Down Expand Up @@ -1069,13 +1150,15 @@ void ScatterplotPlugin::updateData()

// Pass the 2D points to the scatter plot widget
_scatterPlotWidget->setData(&_positions);
_settingsAction->getZOrderingAction().updateScatterplotWidget();

updateSelection();
}
else {
_numPoints = 0;
_positions.clear();
_scatterPlotWidget->setData(&_positions);
_settingsAction->getZOrderingAction().updateScatterplotWidget();
}
}

Expand Down Expand Up @@ -1109,14 +1192,16 @@ void ScatterplotPlugin::updateSelection()

sampledPoints.reserve(_positions.size());

for (auto selectionIndex : selection->indices)
sampledPoints.push_back(selectionIndex);
for (std::uint32_t localIndex = 0; localIndex < selected.size(); ++localIndex) {
if (selected[localIndex] && !_scatterPlotWidget->isSelectionExcluded(localIndex))
sampledPoints.push_back(localIndex);
}

std::int32_t numberOfPoints = 0;

QVariantList localPointIndices, globalPointIndices;

const auto numberOfSelectedPoints = selection->indices.size();
const auto numberOfSelectedPoints = sampledPoints.size();

localPointIndices.reserve(static_cast<std::int32_t>(numberOfSelectedPoints));
globalPointIndices.reserve(static_cast<std::int32_t>(numberOfSelectedPoints));
Expand Down Expand Up @@ -1152,6 +1237,8 @@ void ScatterplotPlugin::updateSelection()
{ "RenderMode", _settingsAction->getRenderModeAction().getCurrentText() }
});
}

updateHeadsUpDisplay();
}

void ScatterplotPlugin::updateHeadsUpDisplay()
Expand Down Expand Up @@ -1181,6 +1268,16 @@ void ScatterplotPlugin::updateHeadsUpDisplay()
//qDebug() << "ScatterplotPlugin::updateHeadsUpDisplay: point size dataset: " << pointPlotAction.getSizeAction().getCurrentDataset().isValid() << ", opacity dataset: " << pointPlotAction.getOpacityAction().getCurrentDataset().isValid();
addMetaDataToHeadsUpDisplay("Size", pointPlotAction.getSizeAction().getCurrentDataset(), datasetsItem);
addMetaDataToHeadsUpDisplay("Opacity", pointPlotAction.getOpacityAction().getCurrentDataset(), datasetsItem);

const auto selectionItem = getHeadsUpDisplayAction().addHeadsUpDisplayItem("Selection", "", "");
const auto numberOfSelectedPoints = _scatterPlotWidget->getNumberOfEffectivelySelectedPoints();
const auto numberOfSelectablePoints = _scatterPlotWidget->getNumberOfSelectablePoints();

getHeadsUpDisplayAction().addHeadsUpDisplayItem(
"Selected:",
QString("%1 of %2 selectable points").arg(numberOfSelectedPoints).arg(numberOfSelectablePoints),
"",
selectionItem);
} else {
getHeadsUpDisplayAction().addHeadsUpDisplayItem("No datasets loaded", "", "");
}
Expand Down
6 changes: 5 additions & 1 deletion src/ScatterplotPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class ScatterplotPlugin : public ViewPlugin

public:
void createSubset(const bool& fromSourceData = false, const QString& name = "");
void selectAllEligiblePoints();
void invertEligiblePointSelection();
void refreshSelection();

public: // Dimension picking
void setXDimension(const std::int32_t& dimensionIndex);
Expand Down Expand Up @@ -111,6 +114,7 @@ class ScatterplotPlugin : public ViewPlugin
void updateData();
void updateSelection();
void updateHeadsUpDisplayTextColor();
void filterSelectionExcludedIndices(std::vector<std::uint32_t>& globalIndices) const;

public:

Expand Down Expand Up @@ -190,4 +194,4 @@ class ScatterplotPluginFactory : public ViewPluginFactory
* @return URL of the GitHub repository (or readme markdown URL if set)
*/
QUrl getRepositoryUrl() const override;
};
};
Loading
Loading