/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2011-2024 Jose Luis Blanco (joseluisblancoc@gmail.com). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "../../utils.h" // PointCloud<> mrpt::opengl::CPointCloud::Ptr pc_to_viz(const PointCloud& pc) { auto gl = mrpt::opengl::CPointCloud::Create(); for (const auto pt : pc.pts) { gl->insertPoint(pt.x, pt.y, pt.z); } gl->setPointSize(3.0f); return gl; } void kdtree_demo(const size_t N) { mrpt::gui::CDisplayWindow3D win("nanoflann example GUI", 800, 600); PointCloud cloud; const double maxRangeXY = 100, maxRangeZ = 100; // Generate points: generateRandomPointCloudRanges(cloud, N, maxRangeXY, maxRangeXY, maxRangeZ); // viz: auto glQueryPt = mrpt::opengl::CPointCloud::Create(); auto glFoundPts = mrpt::opengl::CPointCloud::Create(); auto glQuerySphere = mrpt::opengl::CSphere::Create(); { mrpt::opengl::COpenGLScene::Ptr scene; mrpt::gui::CDisplayWindow3DLocker lck(win, scene); auto glPts = pc_to_viz(cloud); scene->insert(glPts); scene->insert(mrpt::opengl::stock_objects::CornerXYZSimple()); glQueryPt->setPointSize(10.0f); glQueryPt->setColor_u8(0xff, 0x00, 0x00); scene->insert(glQueryPt); glFoundPts->setPointSize(7.0f); glFoundPts->setColor_u8(0x00, 0x00, 0xff, 0x80); scene->insert(glFoundPts); #if defined(USE_RADIUS_SEARCH) || defined(USE_RKNN_SEARCH) glQuerySphere->setColor_u8(0xe0, 0xe0, 0xe0, 0x30); glQuerySphere->enableDrawSolid3D(true); #else glQuerySphere->setColor_u8(0xe0, 0xe0, 0xe0, 0x30); glQuerySphere->enableDrawSolid3D(false); #endif scene->insert(glQuerySphere); } // construct a kd-tree index: using my_kd_tree_t = nanoflann::KDTreeSingleIndexAdaptor< nanoflann::L2_Simple_Adaptor>, PointCloud, 3 /* dim */ >; mrpt::system::CTimeLogger profiler; mrpt::system::CTimeLoggerEntry tle(profiler, "build_kd_tree"); my_kd_tree_t index(3 /*dim*/, cloud, {10 /* max leaf */}); tle.stop(); auto& rng = mrpt::random::getRandomGenerator(); // Declare here to avoid reallocations: #if defined(USE_RADIUS_SEARCH) std::vector> indicesDists; #elif defined(USE_KNN_SEARCH) || defined(USE_RKNN_SEARCH) std::vector indices; std::vector distances; #else #error Expected either KNN, radius, or RKNN search build flag! #endif // Loop: different searches until the window is closed: while (win.isOpen()) { // Unsorted radius search: #if defined(USE_RADIUS_SEARCH) || defined(USE_RKNN_SEARCH) const double radius = rng.drawUniform(0.1, maxRangeXY * 0.5); const double sqRadius = radius * radius; #endif #if defined(USE_KNN_SEARCH) || defined(USE_RKNN_SEARCH) const size_t nnToSearch = (rng.drawUniform32bit() % 10) + 1; #endif const double queryPt[3] = { rng.drawUniform(-0.3, maxRangeXY + 0.3), rng.drawUniform(-0.3, maxRangeXY + 0.3), rng.drawUniform(-0.3, maxRangeZ + 0.3)}; mrpt::system::CTimeLoggerEntry tle2(profiler, "query"); #if defined(USE_RADIUS_SEARCH) indicesDists.clear(); nanoflann::RadiusResultSet resultSet( sqRadius, indicesDists); index.findNeighbors(resultSet, queryPt); #else #if defined(USE_RKNN_SEARCH) nanoflann::RKNNResultSet resultSet( nnToSearch, sqRadius); #elif defined(USE_KNN_SEARCH) nanoflann::KNNResultSet resultSet(nnToSearch); #else #error Should not reach here! #endif indices.resize(nnToSearch); distances.resize(nnToSearch); resultSet.init(indices.data(), distances.data()); index.findNeighbors(resultSet, queryPt); indices.resize(resultSet.size()); distances.resize(resultSet.size()); const double worstDist = std::sqrt(resultSet.worstDist()); #endif tle2.stop(); std::cout << "\nQuery point: (" << queryPt[0] << "," << queryPt[1] << "," << queryPt[2] << ") => " << resultSet.size() << " results.\n"; if (!resultSet.empty()) { #if defined(USE_RADIUS_SEARCH) nanoflann::ResultItem worstPair = resultSet.worst_item(); std::cout << "Worst pair: idx=" << worstPair.first << " dist=" << std::sqrt(worstPair.second) << std::endl; #else std::cout << "nnToSearch=" << nnToSearch << " actual found=" << indices.size() << " Worst found dist=" << worstDist << std::endl; #endif } // Color results: { win.get3DSceneAndLock(); glQueryPt->clear(); glQueryPt->insertPoint(queryPt[0], queryPt[1], queryPt[2]); glQuerySphere->setLocation(queryPt[0], queryPt[1], queryPt[2]); #if defined(USE_RADIUS_SEARCH) || defined(USE_RKNN_SEARCH) glQuerySphere->setRadius(radius); #else glQuerySphere->setRadius(worstDist); #endif glFoundPts->clear(); #if defined(USE_RADIUS_SEARCH) for (const auto& [idx, dist] : indicesDists) #else for (const auto idx : indices) #endif { const auto& pt = cloud.pts.at(idx); glFoundPts->insertPoint(pt.x, pt.y, pt.z); } win.unlockAccess3DScene(); win.repaint(); } std::cout << "Press any key to pick another random query point. Close " "the GUI window to exit." << std::endl; win.waitForKey(); } } int main() { kdtree_demo(1000); return 0; }