/* $Id$ * * Copyright (c) 2010-2011 Anders Wallin (anders.e.e.wallin "at" gmail.com). * * This file is part of OpenCAMlib * (see https://github.com/aewallin/opencamlib). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef KDTREE_H #define KDTREE_H #include #include #include #include "kdnode.hpp" #include "bbox.hpp" #include "millingcutter.hpp" #include "clpoint.hpp" #include "numeric.hpp" namespace ocl { class Point; class CLPoint; class Triangle; class MillingCutter; /// \brief KDTree spread, a measure of how spread-out a list of triangles are. /// /// simple struct-like class for storing the "spread" or maximum /// extent of a list of triangles. Used by the kd-tree algorithm. class Spread { public: /// constructor Spread(int dim, double v, double s) { d = dim; val = v; start = s; }; /// dimension int d; /// spread-value double val; /// minimum or start value double start; /// comparison of Spread objects. Used for finding the largest spread /// along which the next partition/cut is made. static bool spread_compare(Spread *x, Spread *y) { if (x->val > y->val) return true; else return false; }; }; /// a kd-tree for storing triangles and fast searching for triangles /// that overlap the cutter template class KDTree { public: KDTree() { root = nullptr; } virtual ~KDTree() { // std::cout << " ~KDTree()" << std::endl; delete root; } /// set the bucket-size void setBucketSize(int b){ //std::cout << "KDTree::setBucketSize = " << b << "\n"; bucketSize = b; } /// set the search dimension to the XY-plane void setXYDimensions(){ //std::cout << "KDTree::setXYDimensions()\n"; dimensions.clear(); dimensions.push_back(0); // x dimensions.push_back(1); // x dimensions.push_back(2); // y dimensions.push_back(3); // y } // for drop-cutter search in XY plane /// set search-plane to YZ void setYZDimensions(){ // for X-fibers //std::cout << "KDTree::setYZDimensions()\n"; dimensions.clear(); dimensions.push_back(2); // y dimensions.push_back(3); // y dimensions.push_back(4); // z dimensions.push_back(5); // z } // for X-fibers /// set search plane to XZ void setXZDimensions(){ // for Y-fibers //std::cout << "KDTree::setXZDimensions()\n"; dimensions.clear(); dimensions.push_back(0); // x dimensions.push_back(1); // x dimensions.push_back(4); // z dimensions.push_back(5); // z } // for Y-fibers /// build the kd-tree based on a list of input objects void build(const std::list& list){ //std::cout << "KDTree::build() list.size()= " << list.size() << " \n"; delete root; root = build_node( &list, 0, NULL ); } /// search for overlap with input Bbox bb, return found objects std::list* search( const Bbox& bb ){ assert( !dimensions.empty() ); std::list* tris = new std::list(); this->search_node( tris, bb, root ); return tris; } /// search for overlap with a MillingCutter c positioned at cl, return found objects std::list* search_cutter_overlap(const MillingCutter* c, CLPoint* cl ){ double r = c->getRadius(); // build a bounding-box at the current CL Bbox bb( cl->x-r, cl->x+r, cl->y-r, cl->y+r, cl->z, cl->z+c->getLength() ); return this->search( bb ); } /// string repr std::string str() const; protected: /// build and return a KDNode containing list *tris at depth dep. KDNode* build_node( const std::list *tris, // triangles int dep, // depth of node KDNode *par) { // parent node //std::cout << "KDNode::build_node list.size()=" << tris->size() << "\n"; if (tris->size() == 0 ) { //this is a fatal error. std::cout << "ERROR: KDTree::build_node() called with tris->size()==0 ! \n"; assert(0); return 0; } Spread* spr = calc_spread(tris); // calculate spread in order to know how to cut double cutvalue = spr->start + spr->val/2; // cut in the middle //std::cout << " cutvalue= " << cutvalue << "\n"; if ( (tris->size() <= bucketSize) || isZero_tol( spr->val ) ) { // then return a bucket/leaf node //std::cout << "KDNode::build_node BUCKET list.size()=" << tris->size() << "\n"; KDNode *bucket; // dim cutv parent hi lo triangles depth bucket = new KDNode(spr->d, cutvalue , par , NULL, NULL, tris, dep); assert( bucket->isLeaf ); delete spr; return bucket; // this is the leaf/end of the recursion-tree } // build lists of triangles for hi and lo child nodes std::list* lolist = new std::list(); std::list* hilist = new std::list(); BOOST_FOREACH(BBObj t, *tris) { // loop through each triangle and put it in either lolist or hilist if (t.bb[spr->d] > cutvalue) hilist->push_back(t); else lolist->push_back(t); } /* if (hilist->empty() || lolist->empty()) {// an error ?? std::cout << "kdtree: hilist.size()==0! or lolist.size()==0! \n"; std::cout << "kdtree: tris->size()= " << tris->size()<< "\n"; std::cout << "kdtree: hilist.size()= " << hilist->size()<< "\n"; std::cout << "kdtree: lolist.size()= " << lolist->size()<< "\n"; BOOST_FOREACH(BBObj t, *tris) { std::cout << t << "\n"; std::cout << t.bb << "\n"; } std::cout << "kdtree: spr->d= " << spr->d << "\n"; std::cout << "kdtree: cutvalue= " << cutvalue << "\n"; assert(0); }*/ // create the current node dim value parent hi lo trilist depth KDNode *node = new KDNode(spr->d, cutvalue, par, NULL,NULL,NULL, dep); // create the child-nodes through recursion // list depth parent if (!hilist->empty()) node->hi = build_node(hilist, dep+1, node); //else //std::cout << "hilist empty!\n"; if (!lolist->empty()) { node->lo = build_node(lolist, dep+1, node); } else { //std::cout << "lolist empty!\n"; } lolist->clear(); hilist->clear(); delete spr; delete lolist; delete hilist; return node; // return a new node }; /// calculate the spread of list *tris Spread* calc_spread(const std::list *tris) { std::vector maxval( 6 ); std::vector minval( 6 ); if ( tris->empty() ) { std::cout << " ERROR, KDTree::calc_spread() called with tris->size()==0 ! \n"; assert( 0 ); return NULL; } else { // find out the maximum spread //std::cout << "calc_spread()...\n"; bool first=true; BOOST_FOREACH(BBObj t, *tris) { // check each triangle for (unsigned int m=0;m tbbpoint) minval[point] = tbbpoint; } } } double max = 0; double maxM = 0; // std::vector spreads;// calculate the spread along each dimension for (unsigned int m=0;m max) { max = val; maxM = m; } // spreads.push_back( new Spread(dimensions[m] , // maxval[dimensions[m]]-minval[dimensions[m]], // minval[dimensions[m]] ) ); }// priority-queue could also be used ?? // assert( !spreads.empty() ); //std::cout << " spreads.size()=" << spreads.size() << "\n"; // std::sort(spreads.begin(), spreads.end(), Spread::spread_compare); // sort the list // Spread* s= new Spread(*spreads[0]); // this is the one we want to return // while(!spreads.empty()) delete spreads.back(), spreads.pop_back(); // delete the others //std::cout << "calc_spread() done\n"; return new Spread(dimensions[maxM] , maxval[dimensions[maxM]]-minval[dimensions[maxM]], minval[dimensions[maxM]] ); // return s; // select the biggest spread and return } // end tris->size != 0 } // end spread(); /// search kd-tree starting at *node, looking for overlap with bb, and placing /// found objects in *tris void search_node( std::list *tris, const Bbox& bb, KDNode *node) { if (node->isLeaf ) { // we found a bucket node, so add all triangles and return. BOOST_FOREACH( BBObj t, *(node->tris) ) { tris->push_back(t); } //std::cout << " search_node Leaf bucket tris-size() = " << tris->size() << "\n"; return; // end recursion } else if ( (node->dim % 2) == 0) { // cutting along a min-direction: 0, 2, 4 // not a bucket node, so recursevily search hi/lo branches of KDNode unsigned int maxdim = node->dim+1; if ( node->cutval > bb[maxdim] ) { // search only lo search_node(tris, bb, node->lo ); } else { // need to search both child nodes if (node->hi) search_node(tris, bb, node->hi ); if (node->lo) search_node(tris, bb, node->lo ); } } else { // cutting along a max-dimension: 1,3,5 unsigned int mindim = node->dim-1; if ( node->cutval < bb[mindim] ) { // search only hi search_node(tris, bb, node->hi); } else { // need to search both child nodes if (node->hi) search_node(tris, bb, node->hi); if (node->lo) search_node(tris, bb, node->lo); } } return; // Done. We get here after all the recursive calls above. } // end search_kdtree(); // DATA /// bucket size of tree unsigned int bucketSize; /// pointer to root KDNode KDNode* root; /// the dimensions in this kd-tree std::vector dimensions; }; } // end ocl namespace #endif // end file kdtree.hpp