diff --git a/CMakeLists.txt b/CMakeLists.txt index 39453a5..f3fc5aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,10 @@ find_package(Qt6 COMPONENTS Widgets WebEngineWidgets OpenGL OpenGLWidgets REQUIR find_package(ManiVault COMPONENTS Core PointData ClusterData ColorData ImageData CONFIG QUIET) mv_project_defaults() +if(UNIX AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + find_package(TBB REQUIRED) +endif() + # ----------------------------------------------------------------------------- # Source files # ----------------------------------------------------------------------------- @@ -126,6 +130,10 @@ target_link_libraries(${PROJECT} PRIVATE ManiVault::ClusterData) target_link_libraries(${PROJECT} PRIVATE ManiVault::ImageData) target_link_libraries(${PROJECT} PRIVATE ManiVault::ColorData) +if(UNIX AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(${PROJECT} PRIVATE TBB::tbb) +endif() + # ----------------------------------------------------------------------------- # Target installation # ----------------------------------------------------------------------------- diff --git a/conanfile.py b/conanfile.py index a8ba72b..1980dd0 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 from conans import tools import os import pathlib @@ -72,8 +73,8 @@ def configure(self): pass def system_requirements(self): - # May be needed for macOS or Linux - pass + 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": diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 2f18488..41383a4 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -34,10 +34,28 @@ #include #include #include +#include #include #include #include +#ifdef __cpp_lib_execution +#ifdef __GNUC__ // both TBB and Qt define emit keyword: undef +#undef emit +#endif +#include +#ifdef __GNUC__ // both TBB and Qt define emit keyword: def again +#define emit +#endif +#ifdef NDEBUG +#define MV_SCATTER_PARALLEL_EXECUTION std::execution::par, +#else +#define MV_SCATTER_PARALLEL_EXECUTION std::execution::seq, +#endif +#else +#define MV_SCATTER_PARALLEL_EXECUTION +#endif + #define VIEW_SAMPLING_HTML //#define VIEW_SAMPLING_WIDGET @@ -243,21 +261,34 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : else { 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 - std::uint64_t totalNumIndices = 0; - for (const Cluster& cluster : candidateDataset->getClusters()) - { - totalNumIndices += cluster.getIndices().size(); - } - - std::uint64_t totalNumPoints = 0; - if (_positionDataset->isDerivedData()) - totalNumPoints = _positionSourceDataset->getFullDataset()->getNumPoints(); - else - totalNumPoints = _positionDataset->getFullDataset()->getNumPoints(); - - if (totalNumIndices == totalNumPoints) + // Check that the max index in the cluster data does not exceed the max index of the shown point data + auto getMaxIndex = [](const QVector& clusters) -> std::uint32_t + { + if (clusters.empty()) + return std::numeric_limits::lowest(); + + std::vector clusterIndicesMax(clusters.size()); + + std::transform( + MV_SCATTER_PARALLEL_EXECUTION + clusters.cbegin(), clusters.cend(), + clusterIndicesMax.begin(), + [](const Cluster& cluster) -> std::uint32_t { + const std::vector& indices = cluster.getIndices(); + if (indices.empty()) + return std::numeric_limits::lowest(); + + return *std::ranges::max_element(indices); + }); + + return *std::max_element( + MV_SCATTER_PARALLEL_EXECUTION + clusterIndicesMax.cbegin(), clusterIndicesMax.cend()); + }; + + const auto maxIndex = getMaxIndex(candidateDataset->getClusters()); + + if (maxIndex < numTotalPoints()) { // Use the clusters set for points color dropRegions << new DropWidget::DropRegion(this, "Color", description, "palette", true, [this, candidateDataset]() { @@ -698,13 +729,8 @@ void ScatterplotPlugin::positionDatasetChanged() if (!_positionDataset.isValid()) return; - // Reset dataset references - //_positionSourceDataset.reset(); - - // Set position source dataset reference when the position dataset is derived - //if (_positionDataset->isDerivedData()) _positionSourceDataset = _positionDataset->getSourceDataset(); - + _numPoints = _positionDataset->getNumPoints(); _scatterPlotWidget->getPointRendererNavigator().resetView(true); @@ -713,6 +739,16 @@ void ScatterplotPlugin::positionDatasetChanged() updateData(); } +std::uint64_t ScatterplotPlugin::numTotalPoints() const +{ + if (!_positionDataset.isValid()) + return 0; + + return _positionDataset->isDerivedData() + ? _positionSourceDataset->getFullDataset()->getNumPoints() + : _positionDataset->getFullDataset()->getNumPoints(); +} + bool ScatterplotPlugin::mapColorScalars(const Dataset& pointsColor, const std::uint32_t& dimensionIndex, std::vector& colorScalars) { // Only proceed with valid points dataset @@ -900,11 +936,7 @@ void ScatterplotPlugin::loadColors(const Dataset& clusters) return; // Get global indices from the position dataset - std::uint64_t totalNumPoints = 0; - if (_positionDataset->isDerivedData()) - totalNumPoints = _positionSourceDataset->getFullDataset()->getNumPoints(); - else - totalNumPoints = _positionDataset->getFullDataset()->getNumPoints(); + const std::uint64_t totalNumPoints = numTotalPoints(); // Mapping from local to global indices std::vector globalIndices; @@ -916,18 +948,17 @@ void ScatterplotPlugin::loadColors(const Dataset& clusters) const auto& clusterVec = clusters->getClusters(); - if (totalNumPoints == _numPoints && clusterVec.size() == totalNumPoints) + if (totalNumPoints == _numPoints && static_cast(clusterVec.size()) == totalNumPoints) { - for (size_t i = 0; i < static_cast(clusterVec.size()); i++) + // Each cluster corresponds to one point + for (const auto& cluster : clusterVec) { - const auto& cluster = clusterVec[i]; const auto color = cluster.getColor(); - localColors[cluster.getIndices()[0]] = Vector3f(color.redF(), color.greenF(), color.blueF()); } } - else if(globalIndices.size() == _numPoints) + else { // Loop over all clusters and populate global colors for (const auto& cluster : clusterVec) diff --git a/src/ScatterplotPlugin.h b/src/ScatterplotPlugin.h index ef32af8..c04e6a8 100644 --- a/src/ScatterplotPlugin.h +++ b/src/ScatterplotPlugin.h @@ -141,6 +141,12 @@ class ScatterplotPlugin : public ViewPlugin */ bool mapColorScalars(const Dataset& pointsColor, const std::uint32_t& dimensionIndex, std::vector& colorScalars); + /** + * Number of points in positions data set (might be more than _numPoints) + * @return Number of points in positions data set (might be more than _numPoints) + */ + std::uint64_t numTotalPoints() const; + private: mv::gui::DropWidget* _dropWidget; /** Widget for dropping datasets */ ScatterplotWidget* _scatterPlotWidget; /** The visualization widget */