File indexing completed on 2023-03-17 10:45:25
0001 #ifndef COMMONTOOLS_RECOALGOS_FKDTREE_H
0002 #define COMMONTOOLS_RECOALGOS_FKDTREE_H
0003
0004 #include <vector>
0005 #include <array>
0006 #include <algorithm>
0007 #include <cmath>
0008 #include <utility>
0009 #include "FKDPoint.h"
0010 #include "FQueue.h"
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 namespace {
0021 const std::array<unsigned int, 32> MultiplyDeBruijnBitPosition{{0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16,
0022 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17,
0023 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}};
0024 unsigned int ilog2(unsigned int v) {
0025 v |= v >> 1;
0026 v |= v >> 2;
0027 v |= v >> 4;
0028 v |= v >> 8;
0029 v |= v >> 16;
0030 return MultiplyDeBruijnBitPosition[(unsigned int)(v * 0x07C4ACDDU) >> 27];
0031 }
0032 }
0033
0034 template <class TYPE, unsigned int numberOfDimensions>
0035 class FKDTree {
0036 public:
0037 FKDTree() {
0038 theNumberOfPoints = 0;
0039 theDepth = 0;
0040 }
0041
0042 bool empty() { return theNumberOfPoints == 0; }
0043
0044
0045
0046
0047
0048
0049
0050 void search(const FKDPoint<TYPE, numberOfDimensions>& minPoint,
0051 const FKDPoint<TYPE, numberOfDimensions>& maxPoint,
0052 std::vector<unsigned int>& foundPoints) {
0053
0054
0055
0056
0057 FQueue<unsigned int> indicesToVisit(16);
0058
0059
0060 indicesToVisit.push_back(0);
0061 unsigned int index;
0062 bool intersection;
0063 unsigned int dimension;
0064 unsigned int numberOfindicesToVisitThisDepth;
0065 unsigned int numberOfSonsToVisitNext;
0066 unsigned int firstSonToVisitNext;
0067
0068
0069 for (unsigned int depth = 0; depth < theDepth + 1; ++depth) {
0070
0071 dimension = depth % numberOfDimensions;
0072 numberOfindicesToVisitThisDepth = indicesToVisit.size();
0073
0074 for (unsigned int visitedindicesThisDepth = 0; visitedindicesThisDepth < numberOfindicesToVisitThisDepth;
0075 visitedindicesThisDepth++) {
0076 index = indicesToVisit[visitedindicesThisDepth];
0077
0078 intersection = intersects(index, minPoint, maxPoint, dimension);
0079 firstSonToVisitNext = leftSonIndex(index);
0080
0081 if (intersection) {
0082
0083 if (is_in_the_box(index, minPoint, maxPoint)) {
0084 foundPoints.emplace_back(theIds[index]);
0085 }
0086
0087 numberOfSonsToVisitNext =
0088 (firstSonToVisitNext < theNumberOfPoints) + ((firstSonToVisitNext + 1) < theNumberOfPoints);
0089 } else {
0090
0091 firstSonToVisitNext += (theDimensions[dimension][index] < minPoint[dimension]);
0092
0093 numberOfSonsToVisitNext =
0094 std::min((firstSonToVisitNext < theNumberOfPoints) + ((firstSonToVisitNext + 1) < theNumberOfPoints), 1);
0095 }
0096
0097
0098 for (unsigned int whichSon = 0; whichSon < numberOfSonsToVisitNext; ++whichSon) {
0099 indicesToVisit.push_back(firstSonToVisitNext + whichSon);
0100 }
0101 }
0102
0103 indicesToVisit.pop_front(numberOfindicesToVisitThisDepth);
0104 }
0105 }
0106
0107
0108
0109 void build(std::vector<FKDPoint<TYPE, numberOfDimensions> >& points) {
0110
0111 theNumberOfPoints = points.size();
0112 theDepth = ilog2(theNumberOfPoints);
0113 theIntervalLength.resize(theNumberOfPoints, 0);
0114 theIntervalMin.resize(theNumberOfPoints, 0);
0115 for (unsigned int i = 0; i < numberOfDimensions; ++i)
0116 theDimensions[i].resize(theNumberOfPoints);
0117 theIds.resize(theNumberOfPoints);
0118
0119
0120
0121 unsigned int dimension;
0122 theIntervalMin[0] = 0;
0123 theIntervalLength[0] = theNumberOfPoints;
0124
0125
0126 for (unsigned int depth = 0; depth < theDepth; ++depth) {
0127
0128
0129 dimension = depth % numberOfDimensions;
0130 unsigned int firstIndexInDepth = (1 << depth) - 1;
0131 unsigned int maxDepth = (1 << depth);
0132 for (unsigned int indexInDepth = 0; indexInDepth < maxDepth; ++indexInDepth) {
0133 unsigned int indexInArray = firstIndexInDepth + indexInDepth;
0134 unsigned int leftSonIndexInArray = 2 * indexInArray + 1;
0135 unsigned int rightSonIndexInArray = leftSonIndexInArray + 1;
0136
0137
0138
0139 unsigned int whichElementInInterval = partition_complete_kdtree(theIntervalLength[indexInArray]);
0140
0141
0142 std::nth_element(points.begin() + theIntervalMin[indexInArray],
0143 points.begin() + theIntervalMin[indexInArray] + whichElementInInterval,
0144 points.begin() + theIntervalMin[indexInArray] + theIntervalLength[indexInArray],
0145 [dimension](const FKDPoint<TYPE, numberOfDimensions>& a,
0146 const FKDPoint<TYPE, numberOfDimensions>& b) -> bool {
0147 if (a[dimension] == b[dimension])
0148 return a.getId() < b.getId();
0149 else
0150 return a[dimension] < b[dimension];
0151 });
0152
0153
0154 add_at_position(points[theIntervalMin[indexInArray] + whichElementInInterval], indexInArray);
0155 if (leftSonIndexInArray < theNumberOfPoints) {
0156 theIntervalMin[leftSonIndexInArray] = theIntervalMin[indexInArray];
0157 theIntervalLength[leftSonIndexInArray] = whichElementInInterval;
0158 }
0159
0160 if (rightSonIndexInArray < theNumberOfPoints) {
0161 theIntervalMin[rightSonIndexInArray] = theIntervalMin[indexInArray] + whichElementInInterval + 1;
0162 theIntervalLength[rightSonIndexInArray] = (theIntervalLength[indexInArray] - 1 - whichElementInInterval);
0163 }
0164 }
0165 }
0166
0167 dimension = theDepth % numberOfDimensions;
0168 unsigned int firstIndexInDepth = (1 << theDepth) - 1;
0169 for (unsigned int indexInArray = firstIndexInDepth; indexInArray < theNumberOfPoints; ++indexInArray) {
0170 add_at_position(points[theIntervalMin[indexInArray]], indexInArray);
0171 }
0172 }
0173
0174 std::size_t size() const { return theNumberOfPoints; }
0175
0176 private:
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187 unsigned int partition_complete_kdtree(unsigned int length) {
0188 if (length == 1)
0189 return 0;
0190 unsigned int index = 1 << (ilog2(length));
0191
0192 if ((index / 2) - 1 <= length - index)
0193 return index - 1;
0194 else
0195 return length - index / 2;
0196 }
0197
0198
0199 unsigned int leftSonIndex(unsigned int index) const { return 2 * index + 1; }
0200
0201 unsigned int rightSonIndex(unsigned int index) const { return 2 * index + 2; }
0202
0203
0204 bool intersects(unsigned int index,
0205 const FKDPoint<TYPE, numberOfDimensions>& minPoint,
0206 const FKDPoint<TYPE, numberOfDimensions>& maxPoint,
0207 int dimension) const {
0208 return (theDimensions[dimension][index] <= maxPoint[dimension] &&
0209 theDimensions[dimension][index] >= minPoint[dimension]);
0210 }
0211
0212
0213 bool is_in_the_box(unsigned int index,
0214 const FKDPoint<TYPE, numberOfDimensions>& minPoint,
0215 const FKDPoint<TYPE, numberOfDimensions>& maxPoint) const {
0216 for (unsigned int i = 0; i < numberOfDimensions; ++i) {
0217 if ((theDimensions[i][index] <= maxPoint[i] && theDimensions[i][index] >= minPoint[i]) == false)
0218 return false;
0219 }
0220 return true;
0221 }
0222
0223
0224 void add_at_position(const FKDPoint<TYPE, numberOfDimensions>& point, const unsigned int position) {
0225 for (unsigned int i = 0; i < numberOfDimensions; ++i)
0226 theDimensions[i][position] = point[i];
0227 theIds[position] = point.getId();
0228 }
0229
0230 void add_at_position(FKDPoint<TYPE, numberOfDimensions>&& point, const unsigned int position) {
0231 for (unsigned int i = 0; i < numberOfDimensions; ++i)
0232 theDimensions[i][position] = point[i];
0233 theIds[position] = point.getId();
0234 }
0235
0236 unsigned int theNumberOfPoints;
0237 unsigned int theDepth;
0238
0239
0240 std::array<std::vector<TYPE>, numberOfDimensions> theDimensions;
0241 std::vector<unsigned int> theIntervalLength;
0242 std::vector<unsigned int> theIntervalMin;
0243 std::vector<unsigned int> theIds;
0244 };
0245
0246 #endif