├── .gitignore ├── Makefile ├── README.md ├── sample.cpp └── Octree.h /.gitignore: -------------------------------------------------------------------------------- 1 | sample 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: sample 2 | 3 | sample: sample.cpp Octree.h 4 | g++ -O3 -o sample sample.cpp 5 | 6 | clean: 7 | rm -f sample sample.o 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Octree 2 | ====== 3 | 4 | Octree library in C++. 5 | 6 | Contained entirely in a header file. 7 | 8 | Usage: 9 | ------ 10 | 11 | ```c++ 12 | // Class that holds your data. 13 | class Node 14 | { 15 | ... 16 | }; 17 | 18 | // Initialize octree. 19 | // cellSize is the minimum subdivided cell size. 20 | float min[3] = {0.0f, 0.0f, 0.0f}; 21 | float max[3] = {1.0f + EPSILON, 1.0f + EPSILON, 1.0f + EPSILON}; 22 | float cellSize[3] = {0.1, 0.1, 0.1}; 23 | Octree octree(min, max, cellSize); 24 | 25 | // Add data to the octree. 26 | // Data will be added to the leaves. 27 | for (int i = 0; i < NUM_ELEMENTS; i++) 28 | { 29 | // getCell returns the octree leaf cell (of size 30 | // cellSize or larger) that contains the passed 31 | // position. 32 | Node& n = octree.getCell(position[i]); 33 | // Set your data in the cell. 34 | // e.g. add something to a std::vector, etc. 35 | n. ... = ...; 36 | } 37 | 38 | // Traverse octree. 39 | // Create callback class. 40 | class Callback: public Octree::Callback 41 | { 42 | public: 43 | // State variables. 44 | ... 45 | // Callback functor. 46 | // Returns: true = continue and traverse subcells; false = abort branch. 47 | virtual bool operator()(const float min[3], const float max[3], Node& nodeData) 48 | { 49 | ... 50 | } 51 | }; 52 | // Invoke traversal. 53 | Callback cb; 54 | octree.traverse(&cb); 55 | ``` 56 | 57 | See sample.cpp for a more extensive sample. 58 | 59 | -------------------------------------------------------------------------------- /sample.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // sample.cpp 3 | // 4 | // 5 | // Created by Eduardo Poyart on 6/5/12. 6 | // 7 | 8 | /* 9 | Copyright (c) 2012, Eduardo Poyart. 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are 14 | met: 15 | 16 | * Redistributions of source code must retain the above copyright 17 | notice, this list of conditions and the following disclaimer. 18 | * Redistributions in binary form must reproduce the above 19 | copyright notice, this list of conditions and the following disclaimer 20 | in the documentation and/or other materials provided with the 21 | distribution. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #include 37 | // The Octree library resides in a single header file. 38 | #include "Octree.h" 39 | #include 40 | #include 41 | #include 42 | #include 43 | using namespace std; 44 | 45 | // Any 3D vector implementation can be used, as long as it can be converted 46 | // to float[3]. Here we use MyPoint. 47 | struct MyPoint 48 | { 49 | float x; 50 | float y; 51 | float z; 52 | MyPoint(const MyPoint& p2): x(p2.x), y(p2.y), z(p2.z) {} 53 | MyPoint& operator=(const MyPoint& p2) { x = p2.x; y = p2.y; z = p2.z; return *this;} 54 | MyPoint(float in_x, float in_y, float in_z): x(in_x), y(in_y), z(in_z) {} 55 | MyPoint(const float p2[3]): x(p2[0]), y(p2[1]), z(p2[2]) {} 56 | operator float*() { return &x; } 57 | operator const float*() const { return &x; } 58 | MyPoint operator+(const MyPoint& p2) const { return MyPoint(x+p2.x, y+p2.y, z+p2.z); } 59 | MyPoint operator-(const MyPoint& p2) const { return MyPoint(x-p2.x, y-p2.y, z-p2.z); } 60 | MyPoint operator*(float f) const { return MyPoint(x*f, y*f, z*f); } 61 | float lengthsq() { return x*x + y*y + z*z; } 62 | }; 63 | 64 | // Data that we're manipulating. 65 | struct Particle 66 | { 67 | MyPoint pos; 68 | Particle(float x, float y, float z): pos(x, y, z) {} 69 | }; 70 | 71 | // A node in the quadtree. 72 | struct Node 73 | { 74 | vector particles; 75 | }; 76 | 77 | #define NUM_PARTICLES 10000000 78 | #define R 0.01f 79 | #define EPSILON 0.0001f 80 | 81 | // This class does the work of counting particles while traversing the octree. 82 | class CallbackTraverse: public Octree::Callback 83 | { 84 | public: 85 | int count; 86 | Particle* p0; 87 | virtual bool operator()(const float min[3], const float max[3], Node& n) 88 | { 89 | MyPoint pmin(min), pmax(max); 90 | float cellSizeSq = (pmax - pmin).lengthsq(); 91 | float maxDist = (sqrtf(cellSizeSq) * 0.5f) + R + EPSILON; 92 | 93 | MyPoint center = (pmin + pmax) * 0.5f; 94 | MyPoint vectCenter = center - p0->pos; 95 | float distCenterSq = vectCenter.lengthsq(); 96 | if (distCenterSq > maxDist * maxDist) 97 | return false; // Too far; don't subdivide cell. 98 | 99 | // Iterate through particles in this cell. 100 | vector::const_iterator it = n.particles.begin(); 101 | for (; it != n.particles.end(); it++) 102 | { 103 | Particle* p = *it; 104 | if (p == p0) 105 | continue; 106 | float dsq = (p->pos - p0->pos).lengthsq(); 107 | // If particle is within the radius, increment counter. 108 | if (dsq <= R * R) 109 | count++; 110 | } 111 | // Subdivide cell if needed. 112 | return true; 113 | } 114 | }; 115 | 116 | int main() 117 | { 118 | cout << "Number of particles: " << NUM_PARTICLES << endl; 119 | cout << "Initializing particles, uniform distribution in cube ([0,1], [0,1], [0,1])." << endl; 120 | vector myParticles; 121 | for (int i = 0; i < NUM_PARTICLES; i++) 122 | myParticles.push_back(Particle( 123 | (float)rand()/RAND_MAX, (float)rand()/RAND_MAX, (float)rand()/RAND_MAX)); 124 | 125 | cout << endl; 126 | cout << "Count number of particles within a radius of " << R << "." << endl; 127 | cout << "Output format: 'p: c' where c is the number of particles" << endl; 128 | cout << "around particle p." << endl << endl; 129 | 130 | cout << "Computing result with brute force:" << endl; 131 | for (int i = 0; i < 120; i++) 132 | { 133 | cout << setw(3) << i << ": "; 134 | int count = 0; 135 | MyPoint pi(myParticles[i].pos); 136 | for (int j = 0; j < NUM_PARTICLES; j++) 137 | { 138 | if (i == j) 139 | continue; 140 | MyPoint pj(myParticles[j].pos); 141 | float dsq = (pj - pi).lengthsq(); 142 | if (dsq <= R * R) 143 | count++; 144 | } 145 | cout << count << " "; 146 | if (i % 8 == 7) 147 | cout << endl; 148 | } 149 | 150 | cout << endl << "Building octree." << endl; 151 | // Initialize octree. 152 | // Minimum coordinates. 153 | float min[3] = {0.0f, 0.0f, 0.0f}; 154 | // Maximum coordinates. 155 | float max[3] = {1.0f + EPSILON, 1.0f + EPSILON, 1.0f + EPSILON}; 156 | // Minimum size to use when subdividing cells. 157 | float cellSize[3] = {0.1, 0.1, 0.1}; 158 | Octree octree(min, max, cellSize); 159 | 160 | // Add all particles to the octree. 161 | for (int i = 0; i < NUM_PARTICLES; i++) 162 | { 163 | Node& n = octree.getCell(myParticles[i].pos); 164 | n.particles.push_back(&myParticles[i]); 165 | } 166 | cout << "Building octree done." << endl << endl; 167 | 168 | cout << "Computing result with octree:" << endl; 169 | 170 | for (int i = 0; i < 120; i++) 171 | { 172 | cout << setw(3) << i << ": "; 173 | // Prepare the callback class. 174 | CallbackTraverse ct; 175 | ct.count = 0; 176 | ct.p0 = &myParticles[i]; 177 | // Invoke the traversal. 178 | octree.traverse(&ct); 179 | cout << ct.count << " "; 180 | if (i % 8 == 7) 181 | cout << endl; 182 | } 183 | 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /Octree.h: -------------------------------------------------------------------------------- 1 | // 2 | // Octree.h 3 | // 4 | // Created by Eduardo Poyart on 6/4/12. 5 | // 6 | 7 | /* 8 | Copyright (c) 2012, Eduardo Poyart. 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are 13 | met: 14 | 15 | * Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | * Redistributions in binary form must reproduce the above 18 | copyright notice, this list of conditions and the following disclaimer 19 | in the documentation and/or other materials provided with the 20 | distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #ifndef Octree_h 36 | #define Octree_h 37 | 38 | #include 39 | 40 | #define COMPUTE_SIDE(i, bit, p, mid, newMin, newMax) \ 41 | if (p >= mid) \ 42 | { \ 43 | i |= bit; \ 44 | newMin = mid; \ 45 | } \ 46 | else \ 47 | { \ 48 | newMax = mid; \ 49 | } 50 | 51 | 52 | template 53 | class Octree 54 | { 55 | protected: 56 | struct Point 57 | { 58 | float x; 59 | float y; 60 | float z; 61 | Point(const Point& p2): x(p2.x), y(p2.y), z(p2.z) {} 62 | Point& operator=(const Point& p2) { x = p2.x; y = p2.y; z = p2.z; return *this;} 63 | Point(float in_x, float in_y, float in_z): x(in_x), y(in_y), z(in_z) {} 64 | Point(const float p2[3]): x(p2[0]), y(p2[1]), z(p2[2]) {} 65 | operator float*() { return &x; } 66 | operator const float*() const { return &x; } 67 | Point operator+(const Point& p2) const { return Point(x+p2.x, y+p2.y, z+p2.z); } 68 | Point operator-(const Point& p2) const { return Point(x-p2.x, y-p2.y, z-p2.z); } 69 | Point operator*(float f) const { return Point(x*f, y*f, z*f); } 70 | bool operator< (const Point& p2) const { return x < p2.x && y < p2.y && z < p2.z; } 71 | bool operator>=(const Point& p2) const { return x >= p2.x && y >= p2.y && z >= p2.z; } 72 | }; 73 | 74 | struct OctreeNode 75 | { 76 | N _nodeData; 77 | OctreeNode* _children[8]; 78 | OctreeNode() 79 | { 80 | for (int i = 0; i < 8; i++) 81 | _children[i] = 0; 82 | } 83 | virtual ~OctreeNode() 84 | { 85 | for (int i = 0; i < 8; i++) 86 | if (_children[i]) 87 | delete _children[i]; 88 | } 89 | }; 90 | 91 | Point _min; 92 | Point _max; 93 | Point _cellSize; 94 | OctreeNode* _root; 95 | 96 | public: 97 | Octree(float min[3], float max[3], float cellSize[3]): _min(min), _max(max), _cellSize(cellSize), _root(0) {} 98 | virtual ~Octree() { delete _root; } 99 | 100 | class Callback 101 | { 102 | public: 103 | // Return value: true = continue; false = abort. 104 | virtual bool operator()(const float min[3], const float max[3], N& nodeData) = 0; 105 | }; 106 | 107 | N& getCell(const float pos[3], Callback* callback = NULL) 108 | { 109 | Point ppos(pos); 110 | assert(ppos >= _min && ppos < _max); 111 | Point currMin(_min); 112 | Point currMax(_max); 113 | Point delta = _max - _min; 114 | if (!_root) 115 | _root = new OctreeNode(); 116 | OctreeNode* currNode = _root; 117 | while (delta >= _cellSize) 118 | { 119 | bool shouldContinue = true; 120 | if (callback) 121 | shouldContinue = callback->operator()(currMin, currMax, currNode->_nodeData); 122 | if (!shouldContinue) 123 | break; 124 | Point mid = (delta * 0.5f) + currMin; 125 | Point newMin(currMin); 126 | Point newMax(currMax); 127 | int index = 0; 128 | COMPUTE_SIDE(index, 1, ppos.x, mid.x, newMin.x, newMax.x) 129 | COMPUTE_SIDE(index, 2, ppos.y, mid.y, newMin.y, newMax.y) 130 | COMPUTE_SIDE(index, 4, ppos.z, mid.z, newMin.z, newMax.z) 131 | if (!(currNode->_children[index])) 132 | currNode->_children[index] = new OctreeNode(); 133 | currNode = currNode->_children[index]; 134 | currMin = newMin; 135 | currMax = newMax; 136 | delta = currMax - currMin; 137 | } 138 | return currNode->_nodeData; 139 | } 140 | 141 | void traverse(Callback* callback) 142 | { 143 | assert(callback); 144 | traverseRecursive(callback, _min, _max, _root); 145 | } 146 | 147 | void clear() 148 | { 149 | delete _root; 150 | _root = NULL; 151 | } 152 | 153 | class Iterator 154 | { 155 | public: 156 | Iterator getChild(int i) 157 | { 158 | return Iterator(_currNode->_children[i]); 159 | } 160 | N* getData() 161 | { 162 | if (_currNode) 163 | return &_currNode->_nodeData; 164 | else return NULL; 165 | } 166 | protected: 167 | OctreeNode* _currNode; 168 | Iterator(OctreeNode* node): _currNode(node) {} 169 | friend class Octree; 170 | }; 171 | 172 | Iterator getIterator() 173 | { 174 | return Iterator(_root); 175 | } 176 | 177 | protected: 178 | void traverseRecursive(Callback* callback, const Point& currMin, const Point& currMax, OctreeNode* currNode) 179 | { 180 | if (!currNode) 181 | return; 182 | bool shouldContinue = callback->operator()(currMin, currMax, currNode->_nodeData); 183 | if (!shouldContinue) 184 | return; 185 | Point delta = currMax - currMin; 186 | Point mid = (delta * 0.5f) + currMin; 187 | traverseRecursive(callback, currMin, mid, currNode->_children[0]); 188 | traverseRecursive(callback, Point(mid.x, currMin.y, currMin.z), 189 | Point(currMax.x, mid.y, mid.z), currNode->_children[1]); 190 | traverseRecursive(callback, Point(currMin.x, mid.y, currMin.z), 191 | Point(mid.x, currMax.y, mid.z), currNode->_children[2]); 192 | traverseRecursive(callback, Point(mid.x, mid.y, currMin.z), 193 | Point(currMax.x, currMax.y, mid.z), currNode->_children[3]); 194 | traverseRecursive(callback, Point(currMin.x, currMin.y, mid.z), 195 | Point(mid.x, mid.y, currMax.z), currNode->_children[4]); 196 | traverseRecursive(callback, Point(mid.x, currMin.y, mid.z), 197 | Point(currMax.x, mid.y, currMax.z), currNode->_children[5]); 198 | traverseRecursive(callback, Point(currMin.x, mid.y, mid.z), 199 | Point(mid.x, currMax.y, currMax.z), currNode->_children[6]); 200 | traverseRecursive(callback, mid, currMax, currNode->_children[7]); 201 | } 202 | }; 203 | 204 | #endif 205 | --------------------------------------------------------------------------------