Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ find_package(ManiVault COMPONENTS Core PointData ClusterData ColorData ImageData
set(PLUGIN
src/ScatterplotPlugin.h
src/ScatterplotPlugin.cpp
src/MappingUtils.h
src/MappingUtils.cpp
)

set(UI
Expand Down
109 changes: 109 additions & 0 deletions src/MappingUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "MappingUtils.h"

#include <Dataset.h>
#include <LinkedData.h>
#include <PointData/PointData.h>
#include <Set.h>

#include <algorithm>
#include <cstdint>
#include <functional>
#include <map>
#include <tuple>
#include <ranges>
#include <utility>
#include <vector>

std::pair<const mv::LinkedData*, unsigned int> getSelectionMapping(const mv::Dataset<Points>& source, const mv::Dataset<Points>& target, LinkedDataCondition checkMapping) {
const std::vector<mv::LinkedData>& linkedDatas = source->getLinkedData();

if (linkedDatas.empty())
return { nullptr, 0 } ;

// find linked data between source and target OR source and target's parent, if target is derived and they have the same number of points
if (const auto result = std::ranges::find_if(
linkedDatas,
[&target, &checkMapping](const mv::LinkedData& linkedData) -> bool {
return checkMapping(linkedData, target);
});
result != linkedDatas.end())
{
return {&(*result), target->getNumPoints() };
}

return { nullptr, 0 };
}

std::pair<const mv::LinkedData*, unsigned int> getSelectionMappingColorsToPositions(const mv::Dataset<Points>& colors, const mv::Dataset<Points>& positions) {
auto testTargetAndParent = [](const mv::LinkedData& linkedData, const mv::Dataset<Points>& positions) -> bool {
const mv::Dataset<mv::DatasetImpl> mapTargetData = linkedData.getTargetDataset();
return mapTargetData == positions || parentHasSameNumPoints(mapTargetData, positions);
};

return getSelectionMapping(colors, positions, testTargetAndParent);
}

std::pair<const mv::LinkedData*, unsigned int> getSelectionMappingPositionsToColors(const mv::Dataset<Points>& positions, const mv::Dataset<Points>& colors) {
auto testTarget = [](const mv::LinkedData& linkedData, const mv::Dataset<Points>& colors) -> bool {
return linkedData.getTargetDataset() == colors;
};

auto [mapping, numTargetPoints] = getSelectionMapping(positions, colors, testTarget);

if (mapping && parentHasSameNumPoints(positions, positions)) {
const auto positionsParent = positions->getParent<Points>();
std::tie(mapping, numTargetPoints) = getSelectionMapping(positionsParent, colors, testTarget);
}

return { mapping, numTargetPoints };
}

std::pair<const mv::LinkedData*, unsigned int> getSelectionMappingPositionSourceToColors(const mv::Dataset<Points>& positions, const mv::Dataset<Points>& colors) {
if (!positions->isDerivedData())
return { nullptr, 0 };

const auto fullSourceData = positions->getSourceDataset<Points>()->getFullDataset<Points>();

if(!fullSourceData.isValid())
return { nullptr, 0 };

return getSelectionMappingPositionsToColors(fullSourceData, colors);
}

bool checkSurjectiveMapping(const mv::LinkedData& linkedData, const std::uint32_t numPointsInTarget) {
const std::map<std::uint32_t, std::vector<std::uint32_t>>& linkedMap = linkedData.getMapping().getMap();

std::vector<bool> found(numPointsInTarget, false);
std::uint32_t count = 0;

for (const auto& [key, vec] : linkedMap) {
for (std::uint32_t val : vec) {
if (val >= numPointsInTarget) continue; // Skip values that are too large

if (!found[val]) {
found[val] = true;
if (++count == numPointsInTarget)
return true;
}
}
}

return false; // The previous loop would have returned early if the entire taget set was covered
}

bool checkSelectionMapping(const mv::Dataset<Points>& colors, const mv::Dataset<Points>& positions) {

// Check if there is a mapping
auto [mapping, numTargetPoints] = getSelectionMappingColorsToPositions(colors, positions);

if (!mapping)
std::tie(mapping, numTargetPoints) = getSelectionMappingPositionsToColors(positions, colors);

if (!mapping)
std::tie(mapping, numTargetPoints) = getSelectionMappingPositionSourceToColors(positions, colors);

if (!mapping)
return false;

return true;
}
59 changes: 59 additions & 0 deletions src/MappingUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include <Dataset.h>
#include <LinkedData.h>
#include <PointData/PointData.h>
#include <Set.h>

#include <cstdint>
#include <functional>
#include <utility>

// This only checks the immedeate parent and is deliberately not recursive
// We might consider the latter in the future, but might need to cover edge cases
inline bool parentHasSameNumPoints(const mv::Dataset<mv::DatasetImpl> data, const mv::Dataset<Points>& other) {
if (!data->isDerivedData())
return false;

const auto parent = data->getParent();
if (parent->getDataType() != PointType)
return false;

const auto parentPoints = mv::Dataset<Points>(parent);
return parentPoints->getNumPoints() == other->getNumPoints();
}

// Is the data derived and does it's full source data have same number of points as the other data
inline bool fullSourceHasSameNumPoints(const mv::Dataset<mv::DatasetImpl> data, const mv::Dataset<Points>& other) {
if (!data->isDerivedData())
return false;

return data->getSourceDataset<Points>()->getFullDataset<Points>()->getNumPoints() == other->getNumPoints();
}

using LinkedDataCondition = std::function<bool(const mv::LinkedData& linkedData, const mv::Dataset<Points>& target)>;

/* Returns a mapping (linked data) from source that fulfils a given condition based on target, e.g.
auto checkMapping = [](const mv::LinkedData& linkedData, const mv::Dataset<Points>& target) -> bool {
return linkedData.getTargetDataset() == target;
};
This function will return the first match of the condition
*/
std::pair<const mv::LinkedData*, unsigned int> getSelectionMapping(const mv::Dataset<Points>& source, const mv::Dataset<Points>& target, LinkedDataCondition checkMapping);

// Returns a mapping (linked data) from colors whose target is positions or whose target's parent has the same number of points as positions
std::pair<const mv::LinkedData*, unsigned int> getSelectionMappingColorsToPositions(const mv::Dataset<Points>& colors, const mv::Dataset<Points>& positions);

// Returns a mapping (linked data) from positions whose target is colors or
// a mapping from positions' parent whose target is colors if the number of data points match
std::pair<const mv::LinkedData*, unsigned int> getSelectionMappingPositionsToColors(const mv::Dataset<Points>& positions, const mv::Dataset<Points>& colors);

// Returns a mapping (linked data) from positions' source data whose target is colors
std::pair<const mv::LinkedData*, unsigned int> getSelectionMappingPositionSourceToColors(const mv::Dataset<Points>& positions, const mv::Dataset<Points>& colors);

// Check if the mapping is surjective, i.e. hits all elements in the target
bool checkSurjectiveMapping(const mv::LinkedData& linkedData, const std::uint32_t numPointsInTarget);

// returns whether there is a selection map from colors to positions or positions to colors (or respective parents)
// checks whether the mapping covers all elements in the target
bool checkSelectionMapping(const mv::Dataset<Points>& colors, const mv::Dataset<Points>& positions);
Loading
Loading