-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoctree.hpp
641 lines (588 loc) · 20.2 KB
/
octree.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
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
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
// Copyright (C) <[email protected]>
//
// This file is part of TOOFUS (TOols OFten USued)
//
// It can not be copied and/or distributed without the express
// permission of the authors.
// It is coded for academic purposes.
//
// Note
// Without a license, the code is copyrighted by default.
// People can read the code, but they have no legal right to use it.
// To use the code, you must contact the author directly and ask permission.
#ifndef OCTREE_HPP
#define OCTREE_HPP
#include <cmath>
#include <vector>
struct ot_Point {
double x;
double y;
double z;
size_t index;
void *userDataPtr;
ot_Point() : x(0.0), y(0.0), z(0.0), index(0), userDataPtr(nullptr) {}
ot_Point(double x_, double y_, double z_, size_t index_, void *userDataPtr_)
: x(x_), y(y_), z(z_), index(index_), userDataPtr(userDataPtr_) {}
};
struct ot_Shape {
virtual ~ot_Shape() {}
virtual bool contains(ot_Point &p) = 0;
virtual void translate(double Tx, double Ty, double Tz) = 0;
virtual double Xmin() = 0;
virtual double Xmax() = 0;
virtual double Ymin() = 0;
virtual double Ymax() = 0;
virtual double Zmin() = 0;
virtual double Zmax() = 0;
};
struct ot_Sphere : public ot_Shape {
double x;
double y;
double z;
double r;
ot_Sphere() : x(0.0), y(0.0), z(0.0), r(0.0) {}
ot_Sphere(double x_, double y_, double z_, double r_) : x(x_), y(y_), z(z_), r(r_) {}
/**
* Translates the sphere by the given offsets in the x, y, and z directions.
*
* @param Tx The translation offset along the x-axis.
* @param Ty The translation offset along the y-axis.
* @param Tz The translation offset along the z-axis.
*/
void translate(double Tx, double Ty, double Tz) {
x += Tx;
y += Ty;
z += Tz;
}
/**
* Returns the minimum x-coordinate of the sphere.
* @return The x-coordinate of the minimum point of the sphere.
*/
double Xmin() { return (x - r); }
/**
* Returns the maximum x-coordinate of the sphere.
* @return The x-coordinate of the maximum point of the sphere.
*/
double Xmax() { return (x + r); }
/**
* Returns the minimum y-coordinate of the sphere.
* @return The y-coordinate of the minimum point of the sphere.
*/
double Ymin() { return (y - r); }
/**
* Returns the maximum y-coordinate of the sphere.
* @return The y-coordinate of the maximum point of the sphere.
*/
double Ymax() { return (y + r); }
/**
* Returns the minimum z-coordinate of the sphere.
* @return The z-coordinate of the minimum point of the sphere.
*/
double Zmin() { return (z - r); }
/**
* Returns the maximum z-coordinate of the sphere.
* @return The z-coordinate of the maximum point of the sphere.
*/
double Zmax() { return (z + r); }
/**
* Tests if the given point is within the sphere.
*
* @param p The point to test.
* @return True if the point is within the sphere, false otherwise.
*/
bool contains(ot_Point &p) {
double dx = p.x - x;
double dy = p.y - y;
double dz = p.z - z;
double dd = dx * dx + dy * dy + dz * dz;
// double rr = r * r;
return !(dd > r * r);
}
};
// An Axis Aligned (Bounding or not) Box
struct ot_Box : public ot_Shape {
double xmin;
double ymin;
double zmin;
double xmax;
double ymax;
double zmax;
ot_Box() : xmin(0.0), ymin(0.0), zmin(0.0), xmax(0.0), ymax(0.0), zmax(0.0) {}
ot_Box(double xmin_, double ymin_, double zmin_, double xmax_, double ymax_, double zmax_)
: xmin(xmin_), ymin(ymin_), zmin(zmin_), xmax(xmax_), ymax(ymax_), zmax(zmax_) {}
/**
* Translates the box by the given amount.
*
* @param Tx The x-coordinate translation.
* @param Ty The y-coordinate translation.
* @param Tz The z-coordinate translation.
*
* @return A new box translated by the given coordinates.
*/
ot_Box translated(double Tx, double Ty, double Tz) {
return ot_Box(xmin + Tx, ymin + Ty, zmin + Tz, xmax + Tx, ymax + Ty, zmax + Tz);
}
/**
* Translates the box by the specified amounts in the x, y, and z directions.
*
* @param Tx The translation amount along the x-axis.
* @param Ty The translation amount along the y-axis.
* @param Tz The translation amount along the z-axis.
*/
void translate(double Tx, double Ty, double Tz) {
xmin += Tx;
ymin += Ty;
zmin += Tz;
xmax += Tx;
ymax += Ty;
zmax += Tz;
}
/**
* Returns the minimum x-coordinate of the box.
* @return The x-coordinate of the minimum point of the box.
*/
double Xmin() { return xmin; }
/**
* Returns the maximum x-coordinate of the box.
* @return The x-coordinate of the maximum point of the box.
*/
double Xmax() { return xmax; }
/**
* Returns the minimum y-coordinate of the box.
* @return The y-coordinate of the minimum point of the box.
*/
double Ymin() { return ymin; }
/**
* Returns the maximum y-coordinate of the box.
* @return The y-coordinate of the maximum point of the box.
*/
double Ymax() { return ymax; }
/**
* Returns the minimum z-coordinate of the box.
* @return The z-coordinate of the minimum point of the box.
*/
double Zmin() { return zmin; }
/**
* Returns the maximum z-coordinate of the box.
* @return The z-coordinate of the maximum point of the box.
*/
double Zmax() { return zmax; }
/**
* Check if a point is contained within the box.
* @param p The point to test.
* @return true if the point is contained in the box, false otherwise.
*/
bool contains(ot_Point &p) {
return !(p.x < xmin || p.x > xmax || p.y < ymin || p.y > ymax || p.z < zmin || p.z > zmax);
}
/**
* Checks if the box intersects with another box.
*
* @param range The box to test intersection with.
* @return true if the boxes intersect, false otherwise.
*/
bool intersects(ot_Box &range) {
return !(xmin > range.xmax || xmax < range.xmin || ymin > range.ymax || ymax < range.ymin || zmin > range.zmax ||
zmax < range.zmin);
}
bool intersects(ot_Sphere &c) {
double dx = c.x - std::max(xmin, std::min(c.x, xmax));
/**
* Checks if the box intersects with a sphere.
*
* The algorithm works by calculating the distance between the center of the sphere
* and the closest point on the box. If the distance is less than the radius of the
* sphere, then the sphere intersects with the box.
*
* @param c The sphere to test intersection with.
* @return true if the sphere intersects with the box, false otherwise.
*/
double dy = c.y - std::max(ymin, std::min(c.y, ymax));
double dz = c.z - std::max(zmin, std::min(c.z, zmax));
return (dx * dx + dy * dy + dz * dz) < (c.r * c.r);
}
};
class OcTree {
private:
ot_Box boundary;
size_t capacity;
std::vector<ot_Point> points;
bool divided;
OcTree *xmin_ymin_zmin;
OcTree *xmax_ymin_zmin;
OcTree *xmax_ymax_zmin;
OcTree *xmin_ymax_zmin;
OcTree *xmin_ymin_zmax;
OcTree *xmax_ymin_zmax;
OcTree *xmax_ymax_zmax;
OcTree *xmin_ymax_zmax;
/**
* Subdivides the current octree node into eight child nodes.
*
* This method splits the current node's bounding box into eight equal-sized
* smaller boxes by calculating the midpoints of its edges. Each smaller box
* becomes a child node of the current octree. This process allows the octree
* to represent space at finer granularity and handle more points efficiently.
* After subdivision, the 'divided' flag is set to true, indicating that the
* node has been subdivided.
*/
void subdivide() {
double xmin = boundary.xmin;
double ymin = boundary.ymin;
double zmin = boundary.zmin;
double xmax = boundary.xmax;
double ymax = boundary.ymax;
double zmax = boundary.zmax;
double xmid = 0.5 * (xmin + xmax);
double ymid = 0.5 * (ymin + ymax);
double zmid = 0.5 * (zmin + zmax);
xmin_ymin_zmin = new OcTree(xmin, ymin, zmin, xmid, ymid, zmid, capacity);
xmax_ymin_zmin = new OcTree(xmid, ymin, zmin, xmax, ymid, zmid, capacity);
xmax_ymax_zmin = new OcTree(xmid, ymid, zmin, xmax, ymax, zmid, capacity);
xmin_ymax_zmin = new OcTree(xmin, ymid, zmin, xmid, ymax, zmid, capacity);
xmin_ymin_zmax = new OcTree(xmin, ymin, zmid, xmid, ymid, zmax, capacity);
xmax_ymin_zmax = new OcTree(xmid, ymin, zmid, xmax, ymid, zmax, capacity);
xmax_ymax_zmax = new OcTree(xmid, ymid, zmid, xmax, ymax, zmax, capacity);
xmin_ymax_zmax = new OcTree(xmin, ymid, zmid, xmid, ymax, zmax, capacity);
divided = true;
}
public:
/**
* Default constructor for the OcTree class.
*
* Initializes an empty octree with a boundary box that has zero volume,
* with all coordinates set to 0.0. The capacity is set to 0, indicating
* that no points can be stored, and the 'divided' flag is set to false,
* indicating that the tree is not subdivided.
*/
OcTree() {
boundary.xmin = 0.0;
boundary.ymin = 0.0;
boundary.zmin = 0.0;
boundary.xmax = 0.0;
boundary.ymax = 0.0;
boundary.zmax = 0.0;
capacity = 0;
divided = false;
}
/**
* Initializes an OcTree with a boundary box and a capacity.
*
* This constructor is used to create an OcTree with a specific boundary box
* and a specific capacity (i.e. the maximum number of points that can be
* stored in the tree).
*
* @param boundary_ The boundary box for the OcTree.
* @param capacity_ The capacity of the OcTree, i.e. the maximum number of
* points that can be stored.
*/
OcTree(ot_Box &boundary_, size_t capacity_) {
boundary = boundary_;
capacity = capacity_;
divided = false;
}
/**
* Initializes an OcTree with a boundary box and a capacity.
*
* This constructor is used to create an OcTree with a specific boundary box
* and a specific capacity (i.e. the maximum number of points that can be
* stored in the tree).
*
* @param xmin_ The minimum x-coordinate of the boundary box.
* @param ymin_ The minimum y-coordinate of the boundary box.
* @param zmin_ The minimum z-coordinate of the boundary box.
* @param xmax_ The maximum x-coordinate of the boundary box.
* @param ymax_ The maximum y-coordinate of the boundary box.
* @param zmax_ The maximum z-coordinate of the boundary box.
* @param capacity_ The capacity of the OcTree, i.e. the maximum number of
* points that can be stored.
*/
OcTree(double xmin_, double ymin_, double zmin_, double xmax_, double ymax_, double zmax_, size_t capacity_) {
boundary.xmin = xmin_;
boundary.ymin = ymin_;
boundary.zmin = zmin_;
boundary.xmax = xmax_;
boundary.ymax = ymax_;
boundary.zmax = zmax_;
capacity = capacity_;
divided = false;
}
/**
* Inserts a point into the OcTree.
*
* This function inserts a point into the OcTree. If the point is not within
* the boundary box of the OcTree, it immediately returns false. If the point
* is within the OcTree, it checks if the OcTree has reached its capacity. If
* it has, it subdivides itself into eight octants, and attempts to insert the
* point into one of the octants. If it cannot insert the point into any of
* the octants, it returns false.
*
* @param point The point to be inserted into the OcTree.
* @return true if the point was inserted, false otherwise.
*/
bool insert(ot_Point &point) {
if (!boundary.contains(point)) {
return false;
}
if (points.size() < capacity) {
points.push_back(point);
return true;
} else {
if (!divided) {
subdivide();
}
if (xmin_ymin_zmin->insert(point)) {
return true;
} else if (xmax_ymin_zmin->insert(point)) {
return true;
} else if (xmax_ymax_zmin->insert(point)) {
return true;
} else if (xmin_ymax_zmin->insert(point)) {
return true;
} else if (xmin_ymin_zmax->insert(point)) {
return true;
} else if (xmax_ymin_zmax->insert(point)) {
return true;
} else if (xmax_ymax_zmax->insert(point)) {
return true;
} else if (xmin_ymax_zmax->insert(point)) {
return true;
}
}
return false;
}
/**
* Queries the OcTree for points within a given range.
*
* This function recursively traverses the OcTree, and for each point within
* the tree, it checks if the point is within the given range. If the point is
* within the range, it adds the point to the given vector.
*
* @param range A reference to the range to query.
* @param found A reference to the vector of points that are within the range.
* @param indexMin The minimum index of points to consider.
*/
template <typename T> void query(T &range, std::vector<ot_Point> &found, size_t indexMin = 0) {
if (!boundary.intersects(range)) {
return;
}
for (size_t i = 0; i < points.size(); i++) {
if (points[i].index < indexMin)
continue;
if (range.contains(points[i])) {
found.push_back(points[i]);
}
}
if (divided) {
xmin_ymin_zmin->query(range, found, indexMin);
xmax_ymin_zmin->query(range, found, indexMin);
xmax_ymax_zmin->query(range, found, indexMin);
xmin_ymax_zmin->query(range, found, indexMin);
xmin_ymin_zmax->query(range, found, indexMin);
xmax_ymin_zmax->query(range, found, indexMin);
xmax_ymax_zmax->query(range, found, indexMin);
xmin_ymax_zmax->query(range, found, indexMin);
}
return;
}
/**
* Queries the OcTree for points within a given range, taking into account
* the periodicity of the box.
*
* @param range A reference to the range to query.
* @param found A reference to the vector of points that are within the range.
* @param indexMin The minimum index of points to consider.
*/
template <typename T> void query_periodic(T &range, std::vector<ot_Point> &found, size_t indexMin = 0) {
double cpyX = 0.0;
double cpyY = 0.0;
double cpyZ = 0.0;
bool cut = false;
if (range.Xmax() > boundary.xmax && range.Xmin() < boundary.xmax) {
cpyX = -1.0;
cut = true;
} else if (range.Xmin() < boundary.xmin && range.Xmax() > boundary.xmin) {
cpyX = 1.0;
cut = true;
}
if (range.Ymax() > boundary.ymax && range.Ymin() < boundary.ymax) {
cpyY = -1.0;
cut = true;
} else if (range.Ymin() < boundary.ymin && range.Ymax() > boundary.ymin) {
cpyY = 1.0;
cut = true;
}
if (range.Zmax() > boundary.zmax && range.Zmin() < boundary.zmax) {
cpyZ = -1.0;
cut = true;
} else if (range.Zmin() < boundary.zmin && range.Zmax() > boundary.zmin) {
cpyZ = 1.0;
cut = true;
}
query(range, found, indexMin);
if (cut == true) {
double LX = cpyX * fabs(boundary.xmax - boundary.xmin);
double LY = cpyY * fabs(boundary.ymax - boundary.ymin);
double LZ = cpyZ * fabs(boundary.zmax - boundary.zmin);
int nbZero = 0;
if (cpyX == 0.0)
nbZero++;
if (cpyY == 0.0)
nbZero++;
if (cpyZ == 0.0)
nbZero++;
// one single copy (intersects a face)
if (nbZero == 2) {
if (cpyX != 0.0) {
T rg = range;
rg.translate(LX, 0.0, 0.0);
query(rg, found, indexMin);
} else if (cpyY != 0.0) {
T rg = range;
rg.translate(0.0, LY, 0.0);
query(rg, found, indexMin);
} else {
T rg = range;
rg.translate(0.0, 0.0, LZ);
query(rg, found, indexMin);
}
} else if (nbZero == 1) { // 3 copies (intersects an edge)
if (cpyZ == 0.0) {
T rg = range;
rg.translate(LX, 0.0, 0.0);
query(rg, found, indexMin);
rg.translate(0.0, LY, 0.0);
query(rg, found, indexMin);
rg.translate(-LX, 0.0, 0.0);
query(rg, found, indexMin);
} else if (cpyY == 0.0) {
T rg = range;
rg.translate(LX, 0.0, 0.0);
query(rg, found, indexMin);
rg.translate(0.0, 0.0, LZ);
query(rg, found, indexMin);
rg.translate(-LX, 0.0, 0.0);
query(rg, found, indexMin);
} else {
T rg = range;
rg.translate(0.0, LY, 0.0);
query(rg, found, indexMin);
rg.translate(0.0, 0.0, LZ);
query(rg, found, indexMin);
rg.translate(0.0, -LY, 0.0);
query(rg, found, indexMin);
}
} else { // 7 copies (intersects a corner)
T rg = range;
rg.translate(LX, 0.0, 0.0);
query(rg, found, indexMin);
rg.translate(0.0, LY, 0.0);
query(rg, found, indexMin);
rg.translate(-LX, 0.0, 0.0);
query(rg, found, indexMin);
rg.translate(0.0, -LY, LZ);
query(rg, found, indexMin);
rg.translate(LX, 0.0, 0.0);
query(rg, found, indexMin);
rg.translate(0.0, LY, 0.0);
query(rg, found, indexMin);
rg.translate(-LX, 0.0, 0.0);
query(rg, found, indexMin);
}
}
}
};
#endif /* end of include guard: OCTREE_HPP */
#if 0
#include <cstdlib>
#include <fstream>
#include <iostream>
#include "ExecChrono.hpp"
int main(int argc, char const *argv[]) {
struct Particle {
double x, y, z;
};
std::vector<Particle> particles;
std::ofstream all("all.txt");
for (size_t i = 0; i < 25000; i++) {
Particle P;
P.x = 1.0 + rand() / (double)RAND_MAX * 98.0;
P.y = 1.0 + rand() / (double)RAND_MAX * 98.0;
P.z = 1.0 + rand() / (double)RAND_MAX * 98.0;
particles.push_back(P);
all << P.x << ' ' << P.y << ' ' << P.z << '\n';
}
OcTree q(0.0, 0.0, 0.0, 100.0, 100.0, 100.0, 4);
for (size_t i = 0; i < particles.size(); i++) {
ot_Point p(particles[i].x, particles[i].y, particles[i].z, i, &particles[i]);
q.insert(p);
}
// ot_Sphere crange(particles[100].x, particles[100].y, particles[100].z, 20);
// std::cout << particles[100].x << ' ' << particles[100].y << ' ' << particles[100].z << '\n';
ot_Sphere crange(4, 4, 4, 20);
//ot_Box crange(-20, -20, -20, 20, 20, 20);
std::vector<ot_Point> found;
q.query_periodic(crange, found, 100);
std::cout << "nb found = " << found.size() << "\n";
std::ofstream ff("found.txt");
for (size_t i = 0; i < found.size(); i++) {
ff << found[i].x << ' ' << found[i].y << ' ' << found[i].z << ' ' << found[i].index << '\n';
}
size_t count;
double distSearch = 2.0;
ExecChrono C1("Brute force, crange = box");
count = 0;
for (size_t i = 0; i < particles.size(); i++) {
for (size_t j = i + 1; j < particles.size(); j++) {
if (fabs(particles[j].x - particles[i].x) < distSearch && fabs(particles[j].y - particles[i].y) < distSearch &&
fabs(particles[j].z - particles[i].z) < distSearch)
count++;
}
}
C1.stop();
std::cout << "count = " << count << '\n';
ExecChrono C2("Octree, crange = box");
count = 0;
for (size_t i = 0; i < particles.size(); i++) {
ot_Box crange(particles[i].x - distSearch, particles[i].y - distSearch, particles[i].z - distSearch,
particles[i].x + distSearch, particles[i].y + distSearch, particles[i].z + distSearch);
std::vector<ot_Point> found;
q.query(crange, found, i + 1);
count += found.size();
}
C2.stop();
std::cout << "count = " << count << '\n';
ExecChrono C3("Octree, crange = sphere");
count = 0;
for (size_t i = 0; i < particles.size(); i++) {
ot_Sphere crange(particles[i].x, particles[i].y, particles[i].z, distSearch);
std::vector<ot_Point> found;
q.query(crange, found, i + 1);
count += found.size();
}
C3.stop();
std::cout << "count = " << count << '\n';
ExecChrono C4("Octree, crange = box, periodic");
count = 0;
for (size_t i = 0; i < particles.size(); i++) {
ot_Box crange(particles[i].x - distSearch, particles[i].y - distSearch, particles[i].z - distSearch,
particles[i].x + distSearch, particles[i].y + distSearch, particles[i].z + distSearch);
std::vector<ot_Point> found;
q.query_periodic(crange, found, i + 1);
count += found.size();
}
C4.stop();
std::cout << "count = " << count << '\n';
ExecChrono C5("Octree, crange = sphere, periodic");
count = 0;
for (size_t i = 0; i < particles.size(); i++) {
ot_Sphere crange(particles[i].x, particles[i].y, particles[i].z, distSearch);
std::vector<ot_Point> found;
q.query_periodic(crange, found, i + 1);
count += found.size();
}
C5.stop();
std::cout << "count = " << count << '\n';
return 0;
}
#endif