forked from erich666/jgt-code
-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.txt
390 lines (269 loc) · 16.2 KB
/
README.txt
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
Neighand: Neighborhood Handling library
Version 0.2, June 2007.
Nicolas Brodu <[email protected]>
Presentation:
-------------
The goal of this library is to find 3D neighbors efficiently. It stores the
position of objects in 3D. The user can then ask what are the neighbor objects
for any given point, within a radius. You may either ask for all the objects
or only the K-nearest neighbors. This problem of finding neighbors is also
known as a locality query, or a neighborhood search.
This library has been designed for performance and ease of use. It consists
of various query methods adapted to dynamic environments where the objects
may move. These methods are explained in the article "Query Sphere Indexing
for Neighborhood Requests".
A short tutorial is presented below, followed by the full API documentation.
Organisation:
-------------
- The "src" subdirectory contains the sources. You may copy the content
wherever you wish, it is self-contained (it uses only the standard C and C++
libraries). Each source file contains a description of what it does, and the
functions are well documented. See below for the file list.
- The "tests" subdirectory contains example programs and a Makefile. These are
not meant to be tutorials, but rather useful tools. However, learning how to
use the library from them is possible too. See below for the file list.
- The file "QuerySphereIndexing.pdf" is a copy of the aforementioned article.
It explains the rationale of the library.
Usage (project setup):
----------------------
- Copy the src/ directory anywhere in your include path
- Include the "neighand.h" file where you need it.
- That's all. The library is header-only, no link is required.
Usage (Programming):
--------------------
- Check that your compiler supports partial template specializations.
The library was tested with g++ 3.4 and g++ 4.1.2 on Debian/Linux.
- Include the "neighand.h" file. Use the neighand namespace if you wish.
- Define one NeighborhoodHandler object with the parameters you need. The
possible template and function arguments are explained below.
- Use the "insert" method to insert objects in the region of interest, the
"update" method to move them, and the "remove" method to remove them if
needed.
- You may then either:
- Find the neighbors of a given point, either all of them or only the
K-nearest ones. The "findNeighbors", "findNearestNeighbors", and
"findNearestNeighbor" methods are what you need then.
- Provide a functor or a callback, that will be called on all the neighbors
found around a given center. The "applyToNeighbors" method is what you
need in this case.
- Please look at the API below for other useful functions. In particular,
you may find the "squaredDistance" method handy for cyclic worlds.
Tutorial:
---------
struct Agent {
int number; // put your fields and methods here
ObjectProxy<Agent>* proxy; // handy reference, this Agent ID
};
...
// Define a non-cyclic world with a 16x16x16 discretization
typedef NeighborhoodHandler<Agent,4,4,4,false,false,false> NH;
// Make to world cover the region of interest from 0 to 100 in each dimension
NH nh(0,0,0,6.25);
// Insert a few objects
...
agent1.proxy = nh.insert(x1, y1, z1, &agent1, ProxiedObjectRemapper<Agent>());
agent2.proxy = nh.insert(x2, y2, z2, &agent2, ProxiedObjectRemapper<Agent>());
...
// Find all objects within distance d from the point at (x,y,z)
vector<Agent*> neighbors;
nh.findNeighbors(x, y, z, d, neighbors);
// Find the closest object from agent1 (but not itself), within d max. distance
NearestNeighbor<Agent> neighbor;
nh.findNearestNeighbor(agent1.proxy, d, &neighbor);
cout << "The nearest neighbor of agent1 is the agent number " << neighbor.object->number << endl;
cout << "It is at d^2=" << neighbor.squaredDistance << " away from agent1" << endl;
API documentation (see also neighand.h for details):
----------------------------------------------------
- Template arguments:
UserObject: The user type, like Agent in the tutorial
exp2div(x/y/z): The power-of-two for the number of cells in each dimension.
Ex: 4 => 16 cells
wrap(X/Y/Z): Whether the world is cyclic or not in each dimension.
layerZ: Ignored, reserved for a future extension
_Allocator: A C++ allocator for getting memory. Defaults to the standard
allocator.
template <typename UserObject, int exp2divx, int exp2divy, int exp2divz, bool wrapX, bool wrapY, bool wrapZ, bool layerZ = false, class _Allocator = std::allocator<ObjectProxy<UserObject> > > NeighborhoodHandler
- Constructor:
min(x|y|z): The corner of the region of interest in world coordinates.
It is especially important in case there is no wrapping
along at least one direction.
cellSize: The edge size of the cubic cells (the same for each
direction). Thus, for non-cyclic worlds,
min[xyz] + cellSize * 2^exp2div[xyz] is the corner with
maximum coordinate for the region of interest.
objectInitCapacity: Initial capacity in number of objects. Memory is reserved
to hold that max amount of objects, and will be increased
if necessary. Use this parameter to avoid run-time memory
allocation on further inserts if you know in advance how
many objects will be stored at most in the database.
precompFile: The name of a binary file that can be used to cache the
precomputations from one run to the other. Default is empty,
so the precomputations are not cached. This should be a
valid file name. Valid files are read, invalid files are
overwritten by a valid one. The file is ONLY used for
initialization, and not accessed anymore after the
constructor returns.
NeighborhoodHandler(float minx, float miny, float minz, float cellSize, uint32_t objectInitCapacity = 1024, const char* precompFile = "")
- Inserting an object
x/y/z: The position of the object
object: A pointer to the object
remapperFunctor: A functor with the (UserObject*, ObjectProxy<UserObject>*)
signature. It is called when a user object is given a new
proxy. See the rationale in neighand.h and why this leads to
important optimizations.
Return: A proxy = a key identifier for this object
template<typename RemapperFunctor> ObjectProxy<UserObject>* insert(float x, float y, float z, UserObject* object, RemapperFunctor remapperFunctor)
- Removing an object
proxy: The proxy of the object to remove
remapperFunctor: See the insert function
template<typename RemapperFunctor> void remove(ObjectProxy<UserObject>* proxy, RemapperFunctor remapperFunctor)
- Updating the position of an object
proxy: The proxy of the object to move
x/y/z: The new position of the object
void update(ObjectProxy<UserObject>* proxy, float x, float y, float z)
- Finding all the neighbors of a given point
x/y/z: The query center
d: The maximum distance to look for neighbors
p: An object proxy used as the query center. In that case the object
itself is excluded from the neighbors. Call the function with
p->x,p->y,p->z if you wish to include this object.
neighbors: A vector of object pointers filled with the neighbors that are
found. Note: The neighbors distances are not provided here simply
because thay are not always computed, and perhaps not necessary
as well in the user application. Use the squaredDistance function
(see below) if you need it.
void findNeighbors(float x, float y, float z, float d, std::vector<UserObject*>& neighbors)
void findNeighbors(ObjectProxy<UserObject>* p, float d, std::vector<UserObject*>& neighbors)
- Finding (at most) the K nearest neighbors of a given point
x/y/z: The query center
d: The maximum distance to look for neighbors
K: The maximum number of neighbors to return.
p: An object proxy used as the query center. See findNeighbors.
neighbor: A vector or an array of objects that holds the neighbors that were
found. The distances are provided in addition to the objects
because they were necessarily already computed.
Return: The number of objects that were found, in case an array was
provided. Use the vector size() method otherwise.
void findNearestNeighbors(float x, float y, float z, float d, std::vector<NearestNeighbor<UserObject> >& neighbor, unsigned int K)
void findNearestNeighbors(ObjectProxy<UserObject>* p, float d, std::vector<NearestNeighbor<UserObject> >& neighbor, unsigned int K)
int findNearestNeighbors(float x, float y, float z, float d, NearestNeighbor<UserObject>* neighbor, unsigned int K)
int findNearestNeighbors(ObjectProxy<UserObject>* p, float d, NearestNeighbor<UserObject>* neighbor, unsigned int K)
- Finding the nearest neighbor of a given point
x/y/z: The query center
d: The maximum distance to look for the neighbor
p: An object proxy used as the query center. See findNeighbors.
neighbor: A pointer to an object for holding the neighbor that is found
together with its squared distance to the query center.
Return: 1 if a neighbor was found, 0 otherwise.
int findNearestNeighbor(float x, float y, float z, float d, NearestNeighbor<UserObject>* neighbor)
int findNearestNeighbor(ObjectProxy<UserObject>* p, float d, NearestNeighbor<UserObject>* neighbor)
- Applying a functor to all the objects in the viciny of a given point
x/y/z: The query center
d: The maximum distance to look for neighbors
f: The functor or callback to call on each neighbor
p: An object proxy used as the query center. See findNeighbors.
Return: The functor object (which might have an internal state, useful for ex.
to count the number of neighbors).
template<typename Functor> Functor applyToNeighbors(ObjectProxy<UserObject>* p, float d, Functor f)
template<typename Functor> Functor applyToNeighbors(float x, float y, float z, FloatType d, Functor f)
typedef void (*Callback)(UserObject* object, void* userData);
void applyToNeighbors(ObjectProxy<UserObject>* p, float d, Callback f, void* userData)
void applyToNeighbors(float x, float y, float z, float d, Callback f, void* userData)
- Applying a functor to all the objects whatever their position
f: The functor or callback to call on each object
Return: The functor object
template<typename Functor> Functor applyToAll(Functor f)
void applyToAll(Callback f, void* userData)
- Compute the squared distance from point differences
dx/dy/dz: The difference between the points along each dimension: x2-x1, etc.
Return: Simply dx*dx+dy*dy+dz*dz when there is no wrapping. However when a
dimension is wrapping it is taken into account, so you'll probably
find this function very convenient in wrapping worlds.
float squaredDistance(float dx, float dy, float dz)
- Get/Set the query method and method auto-detection parameters
method: One of Auto, Sphere, Cube, NonEmpty, or Brute. See the article for
what this means. The default is Auto, that will try to select the best
method for you, but you may force one of them is the Auto detection
does not give the best results (some user applications are better
suited to some methods).
weight: Additional weight factors to help the auto-detection routine. For
example your architecture may be advantageous to the Brute-force
method and in that case give it a weight <1.0
Return: The current method and weights
void setQueryMethod(QueryMethod method); QueryMethod getQueryMethod()
void setWeightSphere(float weight); float getWeightSphere()
void setWeightCube(float weight); float getWeightCube()
void setWeightNonEmpty(float weight); float getWeightNonEmpty()
void setWeightBrute(float weight); float getWeightBrute()
- Get the automatic selection routine estimated cost factors
x/y/z: The query center
d: The maximum distance to look for neighbors
factorX: Will hold the estimated cost of the method X on return
Return: The method that would be chosen by the automatic selection routine in
this situation. If you don't like this choice then you may weight the
methods using the setWeight functions.
QueryMethod getAutoFactors(float x, float y, float z, float d, float& factorSphere, float& factorCube, float& factorNonEmpty, float& factorBrute)
QueryMethod getAutoFactorsClosest(float x, float y, float z, float d, int N, float& factorSphere, float& factorCube, float& factorNonEmpty, float& factorBrute)
- Get statistics (only available if NEIGHAND_SELECT_METHOD_STAT is defined)
These counters are incremented each time the corresponding method is used.
uint32_t statSphere, statCube, statNonEmpty, statBrute;
void resetStat();
Source files:
-------------
neighand.h: The main header file and the only one you need to include
neighand_structures.hpp: Declare the types like ObjectProxy, QueryMethod, etc.
neighand_apply.hpp: Main query routine, applies a functor to neighbors
neighand_apply_all.hpp: Implements the corresponding function
neighand_apply_checkdist.hpp: Subroutine for neighand_apply.hpp
neighand_apply_processlines_cond.hpp: Subroutine, conditional object inclusion
neighand_apply_processlines_uncond.hpp: Unconditional object inclusion
neighand_closest.hpp: Similar to neighand_apply but for K nearest neighbors
neighand_closest_processlines.hpp: Subroutine for neighand_closest.hpp
neighand.hpp: Implements the other declarations of the main header
neighand_helpers.hpp: Common utilities to all wrapping cases
neighand_wraphelper_ffff.hpp: Specialized routines for the no wrapping case
neighand_wraphelper_tttf.hpp: Specialized routines for the all-wrapping case
neighand_wraphelper_ttff.hpp: Specialized routines for wrapping along X and Y
Test files:
-----------
consistencyTest.cpp: Checks that the library gives correct results. It's
probably a good idea to call this test at least once for
each set of compilation options you're using.
perfTest.cpp: Used to generate the histograms in the article. Might be useful
too to get an idea how the automatic selection routine performs
on your system, though you should instead really call the
getAutoFactors for a representative case of your application
distknn.cpp: Used to generate the plot in the article
lq.c/lq.h: Locality query routine from the OpenSteer project. Similar to the
Cube query method in the non-wrapping case.
lq.c.diff: Change from the OpenSteer version so as to make lq.c compile
independently. Also contains a bug correction not yet in the
OpenSteer CVS, as of 07 June 07.
libkdtree++: Slighly patched 0.2.1 version of the library providing
a kd-tree C++ implementation with an interface similar to
the STL.
libkdtree++-0.2.1.diff: The exact changes operated on the library
kdtree++: Symbolic link, so the kdtree++ library is "installed" here.
KDLQConsistencyTest.cpp: Checks that the kd-tree and the lq bin-lattice files
work as intended.
Makefile: Instructions for compiling the test programs, and in particular the
compiler optimization options (default is -O3). Can also run the
consistency and performance tests automatically for all wrapping
cases, just type "make consistencyTest" for example.
History:
--------
v0.2:
- Added support for directly calling the different query methods (sphere
indexing, bin-lattice cube, non-empty cell list, brute-force).
- Improved the algorithm for automatically selecting the best method.
- API improvements and modifications.
- Made the library compatible with user-defined memory allocators.
- Made the library compatible with the aliasing rule.
- Removed support for separate CPP specialization. That feature was a
maintenance nightmare and it was (and still is) simpler to just specialize
the templates in the user project CPP files anyway.
v0.1:
- Initial public release
Happy hacking!
Nicolas Brodu, 2007
Code released according to the GNU LGPL, v2 or above.