|
1 | 1 | #include "ScatterplotPlugin.h" |
2 | 2 |
|
| 3 | +#include "MappingUtils.h" |
3 | 4 | #include "ScatterplotWidget.h" |
4 | 5 |
|
5 | 6 | #include <Application.h> |
@@ -46,107 +47,6 @@ Q_PLUGIN_METADATA(IID "studio.manivault.ScatterplotPlugin") |
46 | 47 | using namespace mv; |
47 | 48 | using namespace mv::util; |
48 | 49 |
|
49 | | -// This only checks the immedeate parent and is deliberately not recursive |
50 | | -// We might consider the latter in the future, but might need to cover edge cases |
51 | | -static bool parentHasSameNumPoints(const mv::Dataset<DatasetImpl> data, const mv::Dataset<Points>& other) { |
52 | | - if (data->isDerivedData()) { |
53 | | - const auto parent = data->getParent(); |
54 | | - if (parent->getDataType() == PointType) { |
55 | | - const auto parentPoints = mv::Dataset<Points>(parent); |
56 | | - return parentPoints->getNumPoints() == other->getNumPoints(); |
57 | | - } |
58 | | - } |
59 | | - return false; |
60 | | -} |
61 | | - |
62 | | -using CheckFunc = std::function<bool(const mv::LinkedData& linkedData, const mv::Dataset<Points>& target)>; |
63 | | - |
64 | | -static std::optional<const mv::LinkedData*> getSelectionMapping(const mv::Dataset<Points>& source, const mv::Dataset<Points>& target, CheckFunc checkMapping) { |
65 | | - const std::vector<mv::LinkedData>& linkedDatas = source->getLinkedData(); |
66 | | - |
67 | | - if (linkedDatas.empty()) |
68 | | - return std::nullopt; |
69 | | - |
70 | | - // 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 |
71 | | - const auto it = std::ranges::find_if(linkedDatas, [&target, &checkMapping](const mv::LinkedData& linkedData) -> bool { |
72 | | - return checkMapping(linkedData, target); |
73 | | - }); |
74 | | - |
75 | | - if (it != linkedDatas.end()) { |
76 | | - return &(*it); // return pointer to the found object |
77 | | - } |
78 | | - |
79 | | - return std::nullopt; // nothing found |
80 | | -} |
81 | | - |
82 | | -static std::optional<const mv::LinkedData*> getSelectionMappingColorsToPositions(const mv::Dataset<Points>& colors, const mv::Dataset<Points>& positions) { |
83 | | - auto testTargetAndParent = [](const mv::LinkedData& linkedData, const mv::Dataset<Points>& positions) -> bool { |
84 | | - const Dataset<DatasetImpl> mapTargetData = linkedData.getTargetDataset(); |
85 | | - return mapTargetData == positions || parentHasSameNumPoints(mapTargetData, positions); |
86 | | - }; |
87 | | - |
88 | | - return getSelectionMapping(colors, positions, testTargetAndParent); |
89 | | -} |
90 | | - |
91 | | -static std::optional<const mv::LinkedData*> getSelectionMappingPositionsToColors(const mv::Dataset<Points>& positions, const mv::Dataset<Points>& colors) { |
92 | | - auto testTarget = [](const mv::LinkedData& linkedData, const mv::Dataset<Points>& colors) -> bool { |
93 | | - return linkedData.getTargetDataset() == colors; |
94 | | - }; |
95 | | - |
96 | | - auto mapping = getSelectionMapping(positions, colors, testTarget); |
97 | | - |
98 | | - if (!mapping.has_value() && parentHasSameNumPoints(positions, positions)) { |
99 | | - const auto positionsParent = positions->getParent<Points>(); |
100 | | - mapping = getSelectionMapping(positionsParent, colors, testTarget); |
101 | | - } |
102 | | - |
103 | | - return mapping; |
104 | | -} |
105 | | - |
106 | | -// Check if the mapping is surjective, i.e. hits all elements in the target |
107 | | -static bool checkSurjectiveMapping(const mv::LinkedData& linkedData, const std::uint32_t numPointsInTarget) { |
108 | | - const std::map<std::uint32_t, std::vector<std::uint32_t>>& linkedMap = linkedData.getMapping().getMap(); |
109 | | - |
110 | | - std::vector<bool> found(numPointsInTarget, false); |
111 | | - std::uint32_t count = 0; |
112 | | - |
113 | | - for (const auto& [key, vec] : linkedMap) { |
114 | | - for (std::uint32_t val : vec) { |
115 | | - if (val >= numPointsInTarget) continue; // Skip values that are too large |
116 | | - |
117 | | - if (!found[val]) { |
118 | | - found[val] = true; |
119 | | - if (++count == numPointsInTarget) |
120 | | - return true; |
121 | | - } |
122 | | - } |
123 | | - } |
124 | | - |
125 | | - return false; // The previous loop would have returned early if the entire taget set was covered |
126 | | -} |
127 | | - |
128 | | -// returns whether there is a selection map from colors to positions or positions to colors (or respective parents) |
129 | | -// checks whether the mapping covers all elements in the target |
130 | | -static bool checkSelectionMapping(const mv::Dataset<Points>& colors, const mv::Dataset<Points>& positions) { |
131 | | - |
132 | | - // Check if there is a mapping |
133 | | - auto mapping = getSelectionMappingColorsToPositions(colors, positions); |
134 | | - auto numTargetPoints = positions->getNumPoints(); |
135 | | - |
136 | | - if (!mapping.has_value() || mapping.value() == nullptr) { |
137 | | - |
138 | | - mapping = getSelectionMappingPositionsToColors(positions, colors); |
139 | | - numTargetPoints = colors->getNumPoints(); |
140 | | - |
141 | | - if (!mapping.has_value() || mapping.value() == nullptr) |
142 | | - return false; |
143 | | - } |
144 | | - |
145 | | - const bool mappingCoversData = checkSurjectiveMapping(*(mapping.value()), numTargetPoints); |
146 | | - |
147 | | - return mappingCoversData; |
148 | | -} |
149 | | - |
150 | 50 | ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : |
151 | 51 | ViewPlugin(factory), |
152 | 52 | _dropWidget(nullptr), |
|
0 commit comments