-
Notifications
You must be signed in to change notification settings - Fork 5
/
SpaceSearcher.hpp
222 lines (197 loc) · 7.41 KB
/
SpaceSearcher.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#pragma once
/** @file SpaceSearcher.hpp
* @brief Define the SpaceSearcher class for making efficient spatial searches.
*/
#include "CME212/Util.hpp"
#include "CME212/Point.hpp"
#include "CME212/BoundingBox.hpp"
#include "MortonCoder.hpp"
/** @class SpaceSearcher
* @brief Class for making spatial searches, which uses the MortonCoder
* class as a backend.
*
* Given a range of data items and a mapping between these
* data items and Points, the SpaceSearcher class can be used to quickly
* iterate over data items which are contained (or almost conatined) inside
* any given BoundingBox.
*
* See "space_search_test.cpp" for a usage example.
*/
template <typename T, int L = 7>
class SpaceSearcher
{
private:
// Implementation types
/** The number of levels in the MortonCoder. This controls the "accuracy" of
* the searching iterators (10 -- high accuracy, 1 -- low accuracy), but
* can also impose more expensive searching.
*/
static constexpr int NumLevels = L;
/** Type of MortonCoder. */
using MortonCoderType = MortonCoder<NumLevels>;
/** Type of the Morton codes. */
using code_type = typename MortonCoderType::code_type;
/** Helper struct: (code_type,T) pair */
struct morton_pair;
public:
////////////////////////////////////
// TYPE DEFINITIONS AND CONSTANTS //
////////////////////////////////////
/** The type of values that are stored and returned in the spacial search */
using value_type = T;
/** Type of iterators, which iterate over items inside a BoundingBox. */
struct NeighborhoodIterator;
/** Synonym for NeighborhoodIterator */
using iterator = NeighborhoodIterator;
using const_iterator = NeighborhoodIterator;
public:
/////////////////
// CONSTRUCTOR //
/////////////////
/** @brief SpaceSearcher Constructor.
*
* For a range of data items of type @a T given by [@a first, @a last)
* and a function object @a t2p that maps between data items and @a Points, we
* arrange the data along a space filling curve which allows all of the data
* contained withing a given bounding box to be iterated over in less than
* linear time.
*
* @param[in] bb The "parent" bounding box which this SpaceSearcher
* functions within. All data and queries should
* be contained within.
* @param[in] t_begin Iterator to first data item of type @a T.
* @param[in] t_end Iterator to one past the last data item.
* @param[in] t2p A functor that maps data items to @a Points.
* Provides an interface equivalent to
* Point t2p(const T& t) const
*
* @pre For all i in [@a first,@a last), @a bb.contains(@a t2p(*i)).
*/
template <typename TIter, typename T2Point>
SpaceSearcher(const Box3D& bb,
TIter first, TIter last, T2Point t2p) {
// HW4: YOUR CODE HERE
}
/** @brief SpaceSearcher Constructor.
*
* For a range of data items of type @a T given by [@a tfirst, @a tlast)
* and a corresponding range of @a Points given by [@a pfirst, @a plast),
* we arrange the data along a space filling curve which allows all of the
* data contained withing a given bounding box to be iterated over in less
* than linear time.
*
* @param[in] bb The "parent" bounding box which this SpaceSearcher
* functions within. All data and queries should
* be contained within.
* @param[in] tfirst Iterator to first data item of type T.
* @param[in] tlast Iterator to one past the last data item.
* @param[in] pfirst Iterator to first Point corresponding to the position
* of the first data item, *tfirst.
* @param[in] tlast Iterator to one past the last @a Point.
*
* @pre std::distance(tfirst,tlast) == std::distance(pfirst,plast).
* @pre For all i in [@a pfirst,@a plast), bb.contains(*i).
*/
template <typename TIter, typename PointIter>
SpaceSearcher(const Box3D& bb,
TIter tfirst, TIter tlast,
PointIter pfirst, PointIter plast) {
// HW4: YOUR CODE HERE
}
///////////////
// Accessors //
///////////////
/** The bounding box this SpaceSearcher functions within. */
Box3D bounding_box() const {
return mc_.bounding_box();
}
//////////////
// Iterator //
//////////////
/** @class SpaceSearcher::NeighborhoodIterator
* @brief NeighborhoodIterator class for data items. A forward iterator.
*
* Iterates over data items of type @a T contained
* within epsilon of a given bounding box.
*/
struct NeighborhoodIterator {
using value_type = T;
using pointer = T*;
using reference = T&;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
// Default constructor
NeighborhoodIterator() = default;
// Iterator operators
const value_type& operator*() const {
return (*i_).value_;
}
NeighborhoodIterator& operator++() {
++i_; fix();
return *this;
}
bool operator==(const NeighborhoodIterator& other) const {
return i_ == other.i_;
}
bool operator!=(const NeighborhoodIterator& other) const {
return !(*this == other);
}
private:
friend SpaceSearcher;
using MortonIter = typename std::vector<morton_pair>::const_iterator;
// RI: i_ == end_ || MortonCoderType::is_in_box(*i_, min_, max_)
MortonIter i_, end_;
code_type min_, max_;
NeighborhoodIterator(MortonIter i, MortonIter end,
code_type min, code_type max)
: i_(i), end_(end), min_(min), max_(max) {
fix();
}
// @post RI
void fix() {
while (i_ < end_) {
code_type c = MortonCoderType::advance_to_box(*i_, min_, max_);
if (c == *i_) break;
i_ = std::lower_bound(i_, end_, c);
}
}
};
/** Iterator to the first item contained
* within some epsilon of a bounding box.
* @param bb The bounding box to iterate over.
* @pre bounding_box.contains(bb)
*/
const_iterator begin(const Box3D& bb) const {
assert(bounding_box().contains(bb));
code_type morton_min = mc_.code(bb.min());
code_type morton_max = mc_.code(bb.max());
auto mit_end = std::lower_bound(z_data_.begin(), z_data_.end(), morton_max);
return NeighborhoodIterator(z_data_.begin(), mit_end, morton_min, morton_max);
}
/** Iterator to one-past-the-last item contained
* within some epsilon of a bounding box.
* @param bb The bounding box to iterate over.
* @pre bounding_box.contains(bb)
*/
const_iterator end(const Box3D& bb) const {
assert(bounding_box().contains(bb));
code_type morton_min = mc_.code(bb.min());
code_type morton_max = mc_.code(bb.max());
auto mit_end = std::lower_bound(z_data_.begin(), z_data_.end(), morton_max);
return NeighborhoodIterator(mit_end, mit_end, morton_min, morton_max);
}
private:
// MortonCoder instance associated with this SpaceSearcher.
MortonCoderType mc_;
// A (code_type,value_type) pair that can be used as a MortonCode
struct morton_pair {
code_type code_;
value_type value_;
// Cast operator to treat a morton_pair as a code_type in std::algorithms
operator const code_type&() const { return code_; }
// HW4: YOUR CODE HERE
};
// Pairs of Morton codes and data items of type T.
// RI: std::is_sorted(z_data_.begin(), z_data_.end())
std::vector<morton_pair> z_data_;
};