/** * @file * @brief Efficient representation of an octree * @author Jan Elsberg. Automation Group, Jacobs University Bremen gGmbH, Germany. * @author Kai Lingemann. Institute of Computer Science, University of Osnabrueck, Germany. * @author Andreas Nuechter. Institute of Computer Science, University of Osnabrueck, Germany. */ #ifndef BOCTREE_H #define BOCTREE_H #include "searchTree.h" #include "point_type.h" #include "data_types.h" #include "allocator.h" #include "limits.h" #include "nnparams.h" #include "globals.icc" #include #include using std::vector; #include using std::deque; #include using std::set; #include using std::list; #include #include #include #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) #define POPCOUNT(mask) __builtin_popcount(mask) #else #define POPCOUNT(mask) _my_popcount_3(mask) #endif #include // to avoid ifdeffing for offset_ptr.get(), use &(*ptr) namespace { namespace ip = boost::interprocess; } // forward declaration template union bitunion; /** * This is our preferred representation for the leaf nodes (as it is the most compact). * BOctTree makes an array of this, the first containing the number of points (not the * number of coordinates) stored. */ template union dunion { T v; unsigned int length; dunion() : length(0) {}; }; // typedefs in combination with templates are weird //typedef dunion pointrep; #define pointrep union dunion /** * This struct represents the nodes of the octree * * child_pointer is a relative pointer to the first child of this node, as it is only * 48 bit this will cause issues on systems with more than 268 TB of memory. All children * of this node must be stored sequentially. If one of the children is a leaf, that * child will be a pointer to however a set of points is represented (pointrep *). * * valid is a bitmask describing whether the corresponding buckets are filled. * * leaf is a bitmask describing whether the correpsonding bucket is a leaf node. * * The representation of the bitmask is somewhat inefficient. We use 16 bits for only * 3^8 possible states, so in essence we could save 3 bits by compression. * */ class bitoct{ public: #ifdef _MSC_VER __int64 child_pointer : 48; unsigned valid : 8; unsigned leaf : 8; #else signed long child_pointer : 48; unsigned valid : 8; unsigned leaf : 8; #endif /** * sets the child pointer of parent so it points to child */ template static inline void link(bitoct &parent, bitunion *child) { parent.child_pointer = (long)((char*)child - (char*)&parent); } /** * Returns the children of this node (given as parent). */ template static inline void getChildren(const bitoct &parent, bitunion* &children) { children = (bitunion*)((char*)&parent + parent.child_pointer); } template inline bitunion* getChild(unsigned char index) { bitunion *children = (bitunion*)((char*)this + this->child_pointer); for (unsigned char i = 0; i < index; i++) { if ( ( 1 << i ) & valid ) { // if ith node exists children++; } } return children; } }; /** * This union combines an octree node with a pointer to a set of points. This allows * us to use both nodes and leaves interchangeably. * * points is a pointer to the point representation in use * * node is simply the octree node * */ template union bitunion { pointrep *points; //union dunion *points; bitoct node; bitunion(pointrep *p) : points(p) {}; bitunion(bitoct b) : node(b) {}; bitunion() : points(0) { node.child_pointer = 0; node.valid = 0; node.leaf = 0; }; // needed for new [] //! Leaf node: links a pointrep array [length+values] to this union, saved as an offset pointer static inline void link(bitunion* leaf, pointrep* points) { // use node child_pointer as offset_ptr, not pointrep leaf->node.child_pointer = (long)((char*)points - (char*)leaf); } //! Leaf node: points in the array inline T* getPoints() const { // absolute pointer //return &(this->points[1].v); // offset pointer return reinterpret_cast( reinterpret_cast((char*)this + node.child_pointer) + 1 ); } //! Leaf node: length in the array inline unsigned int getLength() const { // absolute pointer //return this->points[0].length; // offset pointer return (reinterpret_cast((char*)this + node.child_pointer))[0].length; } //! Leaf node: all points inline pointrep* getPointreps() const { return reinterpret_cast((char*)this + node.child_pointer); } inline bitunion* getChild(unsigned char index) const { bitunion *children = (bitunion*)((char*)this + this->node.child_pointer); for (unsigned char i = 0; i < index; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists children++; } } return children; } inline bool isValid(unsigned char index) { return ( ( 1 << index ) & node.valid ); } /* inline pointrep* getChild(unsigned char index) { bitunion *children = (bitunion*)((char*)this + this->node.child_pointer); return children[index].points; }*/ inline bool childIsLeaf(unsigned char index) { return ( ( 1 << index ) & node.leaf ); // if ith node is leaf get center } }; // initialized in Boctree.cc, sequence intialized on startup extern char amap[8][8]; extern char imap[8][8]; extern char sequence2ci[8][256][8]; // maps preference to index in children array for every valid_mask and every case /** * @brief Octree * * A cubic bounding box is calculated * from the given 3D points. Then it * is recusivly subdivided into smaller * subboxes */ template class BOctTree : public SearchTree { public: BOctTree() { } template BOctTree(P * const* pts, int n, T voxelSize, PointType _pointtype = PointType(), bool _earlystop = false ) : pointtype(_pointtype), earlystop(_earlystop) { alloc = new PackedChunkAllocator; this->voxelSize = voxelSize; this->POINTDIM = pointtype.getPointDim(); mins = alloc->allocate(POINTDIM); maxs = alloc->allocate(POINTDIM); // initialising for (unsigned int i = 0; i < POINTDIM; i++) { mins[i] = pts[0][i]; maxs[i] = pts[0][i]; } for (unsigned int i = 0; i < POINTDIM; i++) { for (int j = 1; j < n; j++) { mins[i] = min(mins[i], (T)pts[j][i]); maxs[i] = max(maxs[i], (T)pts[j][i]); } } center[0] = 0.5 * (mins[0] + maxs[0]); center[1] = 0.5 * (mins[1] + maxs[1]); center[2] = 0.5 * (mins[2] + maxs[2]); size = max(max(0.5 * (maxs[0] - mins[0]), 0.5 * (maxs[1] - mins[1])), 0.5 * (maxs[2] - mins[2])); size += 1.0; // for numerical reasons we increase size // calculate new buckets T newcenter[8][3]; T sizeNew = size / 2.0; for (unsigned char i = 0; i < 8; i++) { childcenter(center, newcenter[i], size, i); } // set up values uroot = alloc->allocate >(); root = &uroot->node; countPointsAndQueueFast(pts, n, newcenter, sizeNew, *root, center); init(); } BOctTree(std::string filename) { alloc = new PackedChunkAllocator; deserialize(filename); init(); } template BOctTree(vector

&pts, T voxelSize, PointType _pointtype = PointType(), bool _earlystop = false) : earlystop(_earlystop) { alloc = new PackedChunkAllocator; this->voxelSize = voxelSize; this->POINTDIM = pointtype.getPointDim(); mins = alloc->allocate(POINTDIM); maxs = alloc->allocate(POINTDIM); // initialising for (unsigned int i = 0; i < POINTDIM; i++) { mins[i] = pts[0][i]; maxs[i] = pts[0][i]; } for (unsigned int i = 0; i < POINTDIM; i++) { for (unsigned int j = 1; j < pts.size(); j++) { mins[i] = min(mins[i], pts[j][i]); maxs[i] = max(maxs[i], pts[j][i]); } } center[0] = 0.5 * (mins[0] + maxs[0]); center[1] = 0.5 * (mins[1] + maxs[1]); center[2] = 0.5 * (mins[2] + maxs[2]); size = max(max(0.5 * (maxs[0] - mins[0]), 0.5 * (maxs[1] - mins[1])), 0.5 * (maxs[2] - mins[2])); size += 1.0; // for numerical reasons we increase size // calculate new buckets T newcenter[8][3]; T sizeNew = size / 2.0; for (unsigned char i = 0; i < 8; i++) { childcenter(center, newcenter[i], size, i); } // set up values uroot = alloc->allocate >(); root = &uroot->node; countPointsAndQueue(pts, newcenter, sizeNew, *root, center); } virtual ~BOctTree() { if(alloc) { delete alloc; } } void init() { // compute maximal depth as well as the size of the smalles leaf real_voxelSize = size; max_depth = 1; while (real_voxelSize > voxelSize) { real_voxelSize = real_voxelSize/2.0; max_depth++; } child_bit_depth = alloc->allocate(max_depth); child_bit_depth_inv = alloc->allocate(max_depth); for(int d=0; d < max_depth; d++) { child_bit_depth[d] = 1 << (max_depth - d - 1); child_bit_depth_inv[d] = ~child_bit_depth[d]; } mult = 1.0/real_voxelSize; add[0] = -center[0] + size; add[1] = -center[1] + size; add[2] = -center[2] + size; largest_index = child_bit_depth[0] * 2 -1; } protected: /** * Serialization critical variables */ //! the root of the octree ip::offset_ptr root; ip::offset_ptr > uroot; //! storing the center T center[3]; //! storing the dimension T size; //! storing the voxel size T voxelSize; //! The real voxelsize of the leaves T real_voxelSize; //! Offset and real voxelsize inverse factor for manipulation points T add[3]; T mult; //! Dimension of each point: 3 (xyz) + N (attributes) unsigned int POINTDIM; //! storing minimal and maximal values for all dimensions ip::offset_ptr mins; ip::offset_ptr maxs; //! Details of point attributes PointType pointtype; //! ? unsigned char max_depth; ip::offset_ptr child_bit_depth; ip::offset_ptr child_bit_depth_inv; int largest_index; /** * Serialization uncritical, runtime relevant variables */ //! Threadlocal storage of parameters used in SearchTree operations static NNParams params[100]; /** * Serialization uncritical, runtime irrelevant variables (constructor-stuff) */ //! Whether to stop subdividing at N<10 nodes or not bool earlystop; //! Allocator used for creating nodes in the constructor Allocator* alloc; public: inline const T* getMins() const { return &(*mins); } inline const T* getMaxs() const { return &(*maxs); } inline const T* getCenter() const { return center; } inline T getSize() const { return size; } inline unsigned int getPointdim() const { return POINTDIM; } inline const bitoct& getRoot() const { return *root; } inline unsigned int getMaxDepth() const { return max_depth; } inline void getCenter(double _center[3]) const { _center[0] = center[0]; _center[1] = center[1]; _center[2] = center[2]; } void GetOctTreeCenter(vector&c) { GetOctTreeCenter(c, *root, center, size); } void GetOctTreeRandom(vector&c) { GetOctTreeRandom(c, *root); } void GetOctTreeRandom(vector&c, unsigned int ptspervoxel) { GetOctTreeRandom(c, ptspervoxel, *root); } void AllPoints(vector &vp) { AllPoints(*BOctTree::root, vp); } long countNodes() { return 1 + countNodes(*root); } // computes number of inner nodes long countLeaves() { return countLeaves(*root); } // computes number of leaves + points long countOctLeaves() { return countOctLeaves(*root); } // computes number of leaves void deserialize(std::string filename ) { char buffer[sizeof(T) * 20]; T *p = reinterpret_cast(buffer); std::ifstream file; file.open (filename.c_str(), std::ios::in | std::ios::binary); // read magic bits file.read(buffer, 2); if ( buffer[0] != 'X' || buffer[1] != 'T') { std::cerr << "Not an octree file!!" << endl; file.close(); return; } // read header pointtype = PointType::deserialize(file); file.read(buffer, 5 * sizeof(T)); voxelSize = p[0]; center[0] = p[1]; center[1] = p[2]; center[2] = p[3]; size = p[4]; file.read(buffer, sizeof(int)); int *ip = reinterpret_cast(buffer); POINTDIM = *ip; mins = alloc->allocate(POINTDIM); maxs = alloc->allocate(POINTDIM); file.read(reinterpret_cast(&(*mins)), POINTDIM * sizeof(T)); file.read(reinterpret_cast(&(*maxs)), POINTDIM * sizeof(T)); // read root node uroot = alloc->allocate >(); root = &uroot->node; deserialize(file, *root); file.close(); } static void deserialize(std::string filename, vector &points ) { char buffer[sizeof(T) * 20]; std::ifstream file; file.open (filename.c_str(), std::ios::in | std::ios::binary); // read magic bits file.read(buffer, 2); if ( buffer[0] != 'X' || buffer[1] != 'T') { std::cerr << "Not an octree file!!" << endl; file.close(); return; } // read header PointType pointtype = PointType::deserialize(file); file.read(buffer, 5 * sizeof(T)); // read over voxelsize, center and size file.read(buffer, sizeof(int)); int *ip = reinterpret_cast(buffer); unsigned int POINTDIM = *ip; file.read(buffer, POINTDIM * sizeof(T)); file.read(buffer, POINTDIM * sizeof(T)); // read root node deserialize(file, points, pointtype); file.close(); } void serialize(std::string filename) { char buffer[sizeof(T) * 20]; T *p = reinterpret_cast(buffer); std::ofstream file; file.open (filename.c_str(), std::ios::out | std::ios::binary); // write magic bits buffer[0] = 'X'; buffer[1] = 'T'; file.write(buffer, 2); // write header pointtype.serialize(file); p[0] = voxelSize; p[1] = center[0]; p[2] = center[1]; p[3] = center[2]; p[4] = size; int *ip = reinterpret_cast(&(buffer[5 * sizeof(T)])); *ip = POINTDIM; file.write(buffer, 5 * sizeof(T) + sizeof(int)); for (unsigned int i = 0; i < POINTDIM; i++) { p[i] = mins[i]; } for (unsigned int i = 0; i < POINTDIM; i++) { p[i+POINTDIM] = maxs[i]; } file.write(buffer, 2*POINTDIM * sizeof(T)); // write root node serialize(file, *root); file.close(); } static PointType readType(std::string filename ) { char buffer[sizeof(T) * 20]; std::ifstream file; file.open (filename.c_str(), std::ios::in | std::ios::binary); // read magic bits file.read(buffer, 2); if ( buffer[0] != 'X' || buffer[1] != 'T') { std::cerr << "Not an octree file!!" << endl; file.close(); return PointType(); } // read header PointType pointtype = PointType::deserialize(file); file.close(); return pointtype; } /** * Picks the first point in depth first order starting from the given node * */ T* pickPoint(bitoct &node) { bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf // absolute pointer //return &(children->points[1].v); // offset pointer return children->getPoints(); } else { // recurse return pickPoint(children->node); } ++children; // next child } } return 0; } static void childcenter(const T *pcenter, T *ccenter, T size, unsigned char i) { switch (i) { case 0: // 000 ccenter[0] = pcenter[0] - size / 2.0; ccenter[1] = pcenter[1] - size / 2.0; ccenter[2] = pcenter[2] - size / 2.0; break; case 1: // 001 ccenter[0] = pcenter[0] + size / 2.0; ccenter[1] = pcenter[1] - size / 2.0; ccenter[2] = pcenter[2] - size / 2.0; break; case 2: // 010 ccenter[0] = pcenter[0] - size / 2.0; ccenter[1] = pcenter[1] + size / 2.0; ccenter[2] = pcenter[2] - size / 2.0; break; case 3: // 011 ccenter[0] = pcenter[0] + size / 2.0; ccenter[1] = pcenter[1] + size / 2.0; ccenter[2] = pcenter[2] - size / 2.0; break; case 4: // 100 ccenter[0] = pcenter[0] - size / 2.0; ccenter[1] = pcenter[1] - size / 2.0; ccenter[2] = pcenter[2] + size / 2.0; break; case 5: // 101 ccenter[0] = pcenter[0] + size / 2.0; ccenter[1] = pcenter[1] - size / 2.0; ccenter[2] = pcenter[2] + size / 2.0; break; case 6: // 110 ccenter[0] = pcenter[0] - size / 2.0; ccenter[1] = pcenter[1] + size / 2.0; ccenter[2] = pcenter[2] + size / 2.0; break; case 7: // 111 ccenter[0] = pcenter[0] + size / 2.0; ccenter[1] = pcenter[1] + size / 2.0; ccenter[2] = pcenter[2] + size / 2.0; break; default: break; } } static void childcenter(int x, int y, int z, int &cx, int &cy, int &cz, char i, int size) { switch (i) { case 0: // 000 cx = x - size ; cy = y - size ; cz = z - size ; break; case 1: // 001 cx = x + size ; cy = y - size ; cz = z - size ; break; case 2: // 010 cx = x - size ; cy = y + size ; cz = z - size ; break; case 3: // 011 cx = x + size ; cy = y + size ; cz = z - size ; break; case 4: // 100 cx = x - size ; cy = y - size ; cz = z + size ; break; case 5: // 101 cx = x + size ; cy = y - size ; cz = z + size ; break; case 6: // 110 cx = x - size ; cy = y + size ; cz = z + size ; break; case 7: // 111 cx = x + size ; cy = y + size ; cz = z + size ; break; default: break; } } protected: void AllPoints( bitoct &node, vector &vp) { bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf get center // absolute pointer //pointrep *points = children->points; // offset pointer pointrep* points = children->getPointreps(); unsigned int length = points[0].length; T *point = &(points[1].v); // first point for(unsigned int iterator = 0; iterator < length; iterator++ ) { //T *p = new T[BOctTree::POINTDIM]; // T *p = new T[3]; // p[0] = point[0]; p[1] = point[1]; p[2] = point[2]; T *p = new T[BOctTree::POINTDIM]; for (unsigned int k = 0; k < BOctTree::POINTDIM; k++) p[k] = point[k]; vp.push_back(p); //glVertex3f( point[0], point[1], point[2]); point+=BOctTree::POINTDIM; } } else { // recurse AllPoints( children->node, vp); } ++children; // next child } } } static void deserialize(std::ifstream &f, vector &vpoints, PointType &pointtype) { char buffer[2]; pointrep *point = new pointrep[pointtype.getPointDim()]; f.read(buffer, 2); bitoct node; node.valid = buffer[0]; node.leaf = buffer[1]; for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf read points pointrep first; f.read(reinterpret_cast(&first), sizeof(pointrep)); unsigned int length = first.length; // read first element, which is the length for (unsigned int k = 0; k < length; k++) { f.read(reinterpret_cast(point), sizeof(pointrep) * pointtype.getPointDim()); // read the points vpoints.push_back( pointtype.createPoint( &(point->v ) ) ); } } else { // write child deserialize(f, vpoints, pointtype); } } } delete [] point; } void deserialize(std::ifstream &f, bitoct &node) { char buffer[2]; f.read(buffer, 2); node.valid = buffer[0]; node.leaf = buffer[1]; unsigned short n_children = POPCOUNT(node.valid); // create children bitunion *children = alloc->allocate >(n_children); bitoct::link(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf read points pointrep first; f.read(reinterpret_cast(&first), sizeof(pointrep)); unsigned int length = first.length; // read first element, which is the length pointrep *points = alloc->allocate (POINTDIM*length + 1); // absolute pointer //children->points = points; // offset pointer bitunion::link(children, points); points[0] = first; points++; f.read(reinterpret_cast(points), sizeof(pointrep) * length * POINTDIM); // read the points } else { // write child deserialize(f, children->node); } ++children; // next child } } } void serialize(std::ofstream &of, bitoct &node) { char buffer[2]; buffer[0] = node.valid; buffer[1] = node.leaf; of.write(buffer, 2); // write children bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf write points // absolute pointer //pointrep *points = children->points; // offset pointer pointrep* points = children->getPointreps(); unsigned int length = points[0].length; of.write(reinterpret_cast(points), sizeof(pointrep) * (length * POINTDIM +1)); } else { // write child serialize(of, children->node); } ++children; // next child } } } void GetOctTreeCenter(vector&c, bitoct &node, T *center, T size) { T ccenter[3]; bitunion *children; bitoct::getChildren(node, children); for (unsigned char i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists childcenter(center, ccenter, size, i); // childrens center if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf get center T * cp = new T[POINTDIM]; // compute average of reflectance values and store in last position of cp pointrep* points = children->getPointreps(); unsigned int length = points[0].length; T *point = &(points[1].v); float reflectance_center = 0.; for(unsigned int iterator = 0; iterator < length; iterator++ ) { reflectance_center += point[POINTDIM-1]; // add current reflectance point+=BOctTree::POINTDIM; } reflectance_center /= length * 1.0; for (unsigned int iterator = 0; iterator < POINTDIM-1; iterator++) { cp[iterator] = ccenter[iterator]; } cp[POINTDIM-1] = reflectance_center; // reflectance is the last dimension in POINTDIM c.push_back(cp); } else { // recurse GetOctTreeCenter(c, children->node, ccenter, size/2.0); } ++children; // next child } } } void GetOctTreeRandom(vector&c, bitoct &node) { bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf // absolute pointer //pointrep *points = children->points; // offset pointer pointrep* points = children->getPointreps(); // new version to ignore leaves with less than 3 points /* if(points[0].length > 2) { for(int tmp = 0; tmp < points[0].length; tmp++) { T *point = &(points[POINTDIM*tmp+1].v); c.push_back(point); } } */ //old version int tmp = rand(points[0].length); T *point = &(points[POINTDIM*tmp+1].v); c.push_back(point); } else { // recurse GetOctTreeRandom(c, children->node); } ++children; // next child } } } void GetOctTreeRandom(vector&c, unsigned int ptspervoxel, bitoct &node) { bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf // absolute pointer //pointrep *points = children->points; // offset pointer pointrep* points = children->getPointreps(); unsigned int length = points[0].length; if (ptspervoxel >= length) { for (unsigned int j = 0; j < length; j++) c.push_back(&(points[POINTDIM*j+1].v)); ++children; // next child continue; } set indices; while(indices.size() < ptspervoxel) { int tmp = rand(length-1); indices.insert(tmp); } for(set::iterator it = indices.begin(); it != indices.end(); it++) c.push_back(&(points[POINTDIM*(*it)+1].v)); } else { // recurse GetOctTreeRandom(c, ptspervoxel, children->node); } ++children; // next child } } } long countNodes(bitoct &node) { long result = 0; bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf //++result; } else { // recurse result += countNodes(children->node) + 1; } ++children; // next child } } return result; } long countLeaves(bitoct &node) { long result = 0; bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf long nrpts = children->getLength(); result += POINTDIM*nrpts; } else { // recurse result += countLeaves(children->node); } ++children; // next child } } return result; } long countOctLeaves(bitoct &node) { long result = 0; bitunion *children; bitoct::getChildren(node, children); for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf result ++; } else { // recurse result += countTrueLeaves(children->node); } ++children; // next child } } return result; } // TODO: is this still needed? nodes and pointreps are all in the Allocator void deletetNodes(bitoct &node) { bitunion *children; bitoct::getChildren(node, children); bool haschildren = false; for (short i = 0; i < 8; i++) { if ( ( 1 << i ) & node.valid ) { // if ith node exists if ( ( 1 << i ) & node.leaf ) { // if ith node is leaf // absolute pointer //delete [] children->points; // offset pointer delete [] children->getPointreps(); } else { // recurse deletetNodes(children->node); } ++children; // next child haschildren = true; } } // delete children if (haschildren) { bitoct::getChildren(node, children); } } template void* branch( bitoct &node, P * const * splitPoints, int n, T _center[3], T _size) { // if bucket is too small stop building tree // ----------------------------------------- if ((_size <= voxelSize) || (earlystop && n <= 10) ) { // copy points pointrep *points = alloc->allocate (POINTDIM*n + 1); points[0].length = n; int i = 1; for (int j = 0; j < n; j++) { for (unsigned int iterator = 0; iterator < POINTDIM; iterator++) { points[i++].v = splitPoints[j][iterator]; } } return points; } // calculate new buckets T newcenter[8][3]; T sizeNew; sizeNew = _size / 2.0; for (unsigned char i = 0; i < 8; i++) { childcenter(_center, newcenter[i], _size, i); } countPointsAndQueueFast(splitPoints, n, newcenter, sizeNew, node, _center); return 0; } template void* branch( bitoct &node, vector &splitPoints, T _center[3], T _size) { // if bucket is too small stop building tree // ----------------------------------------- if ((_size <= voxelSize) || (earlystop && splitPoints.size() <= 10) ) { // copy points pointrep *points = alloc->allocate (POINTDIM*splitPoints.size() + 1); points[0].length = splitPoints.size(); int i = 1; for (typename vector

::iterator itr = splitPoints.begin(); itr != splitPoints.end(); itr++) { for (unsigned int iterator = 0; iterator < POINTDIM; iterator++) { points[i++].v = (*itr)[iterator]; } } return points; } // calculate new buckets T newcenter[8][3]; T sizeNew; sizeNew = _size / 2.0; for (unsigned char i = 0; i < 8; i++) { childcenter(_center, newcenter[i], _size, i); } countPointsAndQueue(splitPoints, newcenter, sizeNew, node, _center); return 0; } template void countPointsAndQueue(vector &i_points, T center[8][3], T size, bitoct &parent, T *pcenter) { vector points[8]; int n_children = 0; for (typename vector

::iterator itr = i_points.begin(); itr != i_points.end(); itr++) { points[childIndex

(pcenter, *itr)].push_back( *itr ); } i_points.clear(); vector().swap(i_points); for (int j = 0; j < 8; j++) { if (!points[j].empty()) { parent.valid = ( 1 << j ) | parent.valid; ++n_children; } } // create children bitunion *children = alloc->allocate >(n_children); bitoct::link(parent, children); int count = 0; for (int j = 0; j < 8; j++) { if (!points[j].empty()) { pointrep *c = (pointrep*)branch(children[count].node, points[j], center[j], size); // leaf node if (c) { // absolute pointer //children[count].points = c; // set this child to deque of points // offset pointer bitunion::link(&children[count], c); parent.leaf = ( 1 << j ) | parent.leaf; // remember this is a leaf } points[j].clear(); vector().swap(points[j]); ++count; } } } template void countPointsAndQueueFast(P * const* points, int n, T center[8][3], T size, bitoct &parent, T pcenter[3]) { P * const *blocks[9]; blocks[0] = points; blocks[8] = points + n; fullsort(points, n, pcenter, blocks+1); int n_children = 0; for (int j = 0; j < 8; j++) { // if non-empty set valid flag for this child if (blocks[j+1] - blocks[j] > 0) { parent.valid = ( 1 << j ) | parent.valid; ++n_children; } } // create children bitunion *children = alloc->allocate >(n_children); bitoct::link(parent, children); int count = 0; for (int j = 0; j < 8; j++) { if (blocks[j+1] - blocks[j] > 0) { pointrep *c = (pointrep*)branch(children[count].node, blocks[j], blocks[j+1] - blocks[j], center[j], size); // leaf node if (c) { // absolute pointer //children[count].points = c; // set this child to vector of points // offset pointer bitunion::link(&children[count], c); // set this child to vector of points parent.leaf = ( 1 << j ) | parent.leaf; // remember this is a leaf } ++count; } } } void getByIndex(T *point, T *&points, unsigned int &length) { unsigned int x,y,z; x = (point[0] + add[0]) * mult; y = (point[1] + add[1]) * mult; z = (point[2] + add[2]) * mult; bitunion *node = uroot; unsigned char child_index; unsigned int child_bit; unsigned int depth = 0; while (true) { child_bit = child_bit_depth[depth]; child_index = ((x & child_bit )!=0) | (((y & child_bit )!=0 )<< 1) | (((z & child_bit )!=0) << 2); if (node->childIsLeaf(child_index) ) { node = node->getChild(child_index); points = node->getPoints(); length = node->getLength(); return; } else { node = node->getChild(child_index); } depth++; } } template inline unsigned char childIndex(const T *center, const P *point) { return (point[0] > center[0] ) | ((point[1] > center[1] ) << 1) | ((point[2] > center[2] ) << 2) ; } /** * Given a leaf node, this function looks for the closest point to params[threadNum].closest * in the list of points. */ inline void findClosestInLeaf(bitunion *node, int threadNum) const { if (params[threadNum].count >= params[threadNum].max_count) return; params[threadNum].count++; T* points = node->getPoints(); unsigned int length = node->getLength(); for(unsigned int iterator = 0; iterator < length; iterator++ ) { double myd2 = Dist2(params[threadNum].p, points); if (myd2 < params[threadNum].closest_d2) { params[threadNum].closest_d2 = myd2; params[threadNum].closest = points; if (myd2 <= 0.0001) { params[threadNum].closest_v = 0; // the search radius in units of voxelSize } else { params[threadNum].closest_v = sqrt(myd2) * mult + 1; // the search radius in units of voxelSize } } points+=BOctTree::POINTDIM; } } /** * This function finds the closest point in the octree given a specified * radius. This implementation is quit complex, although it is already * simplified. The simplification incurs a significant loss in speed, as * several calculations have to be performed repeatedly and a high number of * unnecessary jumps are executed. */ double *FindClosest(double *point, double maxdist2, int threadNum) const { params[threadNum].closest = 0; // no point found currently params[threadNum].closest_d2 = maxdist2; params[threadNum].p = point; params[threadNum].x = (point[0] + add[0]) * mult; params[threadNum].y = (point[1] + add[1]) * mult; params[threadNum].z = (point[2] + add[2]) * mult; params[threadNum].closest_v = sqrt(maxdist2) * mult + 1; // the search radius in units of voxelSize params[threadNum].count = 0; params[threadNum].max_count = 10000; // stop looking after this many buckets // box within bounds in voxel coordinates int xmin, ymin, zmin, xmax, ymax, zmax; xmin = max(params[threadNum].x-params[threadNum].closest_v, 0); ymin = max(params[threadNum].y-params[threadNum].closest_v, 0); zmin = max(params[threadNum].z-params[threadNum].closest_v, 0); // int largest_index = child_bit_depth[0] * 2 -1; xmax = min(params[threadNum].x+params[threadNum].closest_v, largest_index); ymax = min(params[threadNum].y+params[threadNum].closest_v, largest_index); zmax = min(params[threadNum].z+params[threadNum].closest_v, largest_index); unsigned char depth = 0; unsigned int child_bit; unsigned int child_index_min; unsigned int child_index_max; bitunion *node = &(*uroot); int cx, cy, cz; child_bit = child_bit_depth[depth]; cx = child_bit_depth[depth]; cy = child_bit_depth[depth]; cz = child_bit_depth[depth]; while (true) { // find the first node where branching is required child_index_min = ((xmin & child_bit )!=0) | (((ymin & child_bit )!=0 )<< 1) | (((zmin & child_bit )!=0) << 2); child_index_max = ((xmax & child_bit )!=0) | (((ymax & child_bit )!=0 )<< 1) | (((zmax & child_bit )!=0) << 2); // if these are the same, go there // TODO: optimization: also traverse if only single child... if (child_index_min == child_index_max) { if (node->childIsLeaf(child_index_min) ) { // luckily, no branching is required findClosestInLeaf(node->getChild(child_index_min), threadNum); return static_cast(params[threadNum].closest); } else { if (node->isValid(child_index_min) ) { // only descend when there is a child childcenter(cx,cy,cz, cx,cy,cz, child_index_min, child_bit/2 ); node = node->getChild(child_index_min); child_bit /= 2; } else { // there is no child containing the bounding box => no point is close enough return 0; } } } else { // if min and max are not in the same child we must branch break; } } // node contains all box-within-bounds cells, now begin best bin first search _FindClosest(threadNum, node->node, child_bit/2, cx, cy, cz); return static_cast(params[threadNum].closest); } /** * This is the heavy duty search function doing most of the (theoretically unneccesary) work. The tree is recursively searched. * Depending on which of the 8 child-voxels is closer to the query point, the children are examined in a special order. * This order is defined in map, imap is its inverse and sequence2ci is a speedup structure for faster access to the child indices. */ void _FindClosest(int threadNum, bitoct &node, int size, int x, int y, int z) const { // Recursive case // compute which child is closest to the query point unsigned char child_index = ((params[threadNum].x - x) >= 0) | (((params[threadNum].y - y) >= 0) << 1) | (((params[threadNum].z - z) >= 0) << 2); char *seq2ci = sequence2ci[child_index][node.valid]; // maps preference to index in children array char *mmap = amap[child_index]; // maps preference to area index bitunion *children; bitoct::getChildren(node, children); int cx, cy, cz; cx = cy = cz = 0; // just to shut up the compiler warnings for (unsigned char i = 0; i < 8; i++) { // in order of preference child_index = mmap[i]; // the area index of the node if ( ( 1 << child_index ) & node.valid ) { // if ith node exists childcenter(x,y,z, cx,cy,cz, child_index, size); if ( params[threadNum].closest_v == 0 || max(max(abs( cx - params[threadNum].x ), abs( cy - params[threadNum].y )), abs( cz - params[threadNum].z )) - size > params[threadNum].closest_v ) { continue; } // find the closest point in leaf seq2ci[i] if ( ( 1 << child_index ) & node.leaf ) { // if ith node is leaf findClosestInLeaf( &children[seq2ci[i]], threadNum); } else { // recurse _FindClosest(threadNum, children[seq2ci[i]].node, size/2, cx, cy, cz); } } } } /** * This function shows the possible speedup that can be gained by using the * octree for nearest neighbour search, if a more sophisticated * implementation were given. Here, only the bucket in which the query point * falls is looked up. If doing the same thing in the kd-tree search, this * function is about 3-5 times as fast */ double *FindClosestInBucket(double *point, double maxdist2, int threadNum) { params[threadNum].closest = 0; params[threadNum].closest_d2 = maxdist2; params[threadNum].p = point; unsigned int x,y,z; x = (point[0] + add[0]) * mult; y = (point[1] + add[1]) * mult; z = (point[2] + add[2]) * mult; T * points; unsigned int length; bitunion *node = uroot; unsigned char child_index; unsigned int child_bit = child_bit_depth[0]; while (true) { child_index = ((x & child_bit )!=0) | (((y & child_bit )!=0 )<< 1) | (((z & child_bit )!=0) << 2); if (node->childIsLeaf(child_index) ) { node = node->getChild(child_index); points = node->getPoints(); length = node->getLength(); for(unsigned int iterator = 0; iterator < length; iterator++ ) { double myd2 = Dist2(params[threadNum].p, points); if (myd2 < params[threadNum].closest_d2) { params[threadNum].closest_d2 = myd2; params[threadNum].closest = points; } points+=BOctTree::POINTDIM; } return static_cast(params[threadNum].closest); } else { if (node->isValid(child_index) ) { node = node->getChild(child_index); } else { return 0; } } child_bit >>= 1; } return static_cast(params[threadNum].closest); } template void fullsort(P * const * points, int n, T splitval[3], P * const * blocks[9]) { P* const * L0; P* const * L1; P* const * L2; unsigned int n0L, n0R, n1L, n1R ; // sort along Z L0 = sort(points, n, splitval[2], 2); n0L = L0 - points; // sort along Y (left of Z) points -- L0 L1 = sort(points, n0L, splitval[1], 1); n1L = L1 - points; // sort along X (left of Y) points -- L1 L2 = sort(points, n1L, splitval[0], 0); blocks[0] = L2; n1R = n0L - n1L; // sort along X (right of Y) // L1 -- L0 L2 = sort(L1, n1R, splitval[0], 0); blocks[1] = L1; blocks[2] = L2; n0R = n - n0L; // sort along Y (right of Z) L0 -- end L1 = sort(L0, n0R, splitval[1], 1); n1L = L1 - L0; // sort along X (left of Y) points -- L1 L2 = sort(L0, n1L, splitval[0], 0); blocks[3] = L0; blocks[4] = L2; n1R = n0R - n1L; // sort along X (right of Y) // L1 -- L0 L2 = sort(L1, n1R, splitval[0], 0); blocks[5] = L1; blocks[6] = L2; } template P* const * sort(P* const * points, unsigned int n, T splitval, unsigned char index) { if (n==0) return points; if (n==1) { if (points[0][index] < splitval) return points+1; else return points; } P **left = const_cast(points); P **right = const_cast(points + n - 1); while (1) { while ((*left)[index] < splitval) { left++; if (right < left) break; } while ((*right)[index] >= splitval) { right--; if (right < left) break; } if (right < left) break; std::swap(*left, *right); } return left; } public: /** * Copies another (via new constructed) octtree into cache allocated memory and makes it position independant */ BOctTree(const BOctTree& other, unsigned char* mem_ptr, unsigned int mem_max) { alloc = new SequentialAllocator(mem_ptr, mem_max); // "allocate" space for *this alloc->allocate >(); // take members unsigned int i; for(i = 0; i < 3; ++i) center[i] = other.center[i]; size = other.size; voxelSize = other.voxelSize; real_voxelSize = other.real_voxelSize; for(i = 0; i < 3; ++i) add[i] = other.add[i]; mult = other.mult; POINTDIM = other.POINTDIM; mins = alloc->allocate(POINTDIM); maxs = alloc->allocate(POINTDIM); for(i = 0; i < POINTDIM; ++i) { mins[i] = other.mins[i]; maxs[i] = other.maxs[i]; } pointtype = other.pointtype; max_depth = other.max_depth; child_bit_depth = alloc->allocate(max_depth); child_bit_depth_inv = alloc->allocate(max_depth); for(i = 0; i < max_depth; ++i) { child_bit_depth[i] = other.child_bit_depth[i]; child_bit_depth_inv[i] = other.child_bit_depth_inv[i]; } largest_index = other.largest_index; // take node structure uroot = alloc->allocate >(); root = &uroot->node; copy_children(*other.root, *root); // test if allocator has used up his space //alloc->printSize(); // discard allocator, space is managed by the cache manager delete alloc; alloc = 0; } private: void copy_children(const bitoct& other, bitoct& my) { // copy node attributes my.valid = other.valid; my.leaf = other.leaf; // other children bitunion* other_children; bitoct::getChildren(other, other_children); // create own children unsigned int n_children = POPCOUNT(other.valid); bitunion* my_children = alloc->allocate >(n_children); bitoct::link(my, my_children); // iterate over all (valid) children and copy them for(unsigned int i = 0; i < 8; ++i) { if((1<getLength(); pointrep* other_pointreps = other_children->getPointreps(); pointrep* my_pointreps = alloc->allocate(POINTDIM * length + 1); for(unsigned int j = 0; j < POINTDIM * length + 1; ++j) my_pointreps[j] = other_pointreps[j]; // assign bitunion::link(my_children, my_pointreps); } else { // child is already created, copy and create children for it copy_children(other_children->node, my_children->node); } ++other_children; ++my_children; } } } public: //! Size of the whole tree structure, including the main class, its serialize critical allocated variables and nodes, not the allocator unsigned int getMemorySize() { return sizeof(*this) // all member variables + 2*POINTDIM*sizeof(T) // mins, maxs + 2*max_depth*sizeof(unsigned int) // child_bit_depth(_inv) + sizeof(bitunion) // uroot + sizeChildren(*root); // all nodes } private: //! Recursive size of a node's children unsigned int sizeChildren(const bitoct& node) { unsigned int s = 0; bitunion* children; bitoct::getChildren(node, children); // size of children allocation unsigned int n_children = POPCOUNT(node.valid); s += sizeof(bitunion)*n_children; // iterate over all (valid) children and sum them up for(unsigned int i = 0; i < 8; ++i) { if((1<getLength()*POINTDIM+1); } else { // childe node is already accounted for, add its children s += sizeChildren(children->node); } ++children; // next (valid) child } } return s; } }; typedef SingleObject > DataOcttree; template NNParams BOctTree::params[100]; #endif