Program Listing for File SparseTreesPointCloud.h
↰ Return to documentation for file (include/mola_metric_maps/SparseTreesPointCloud.h)
/* _
_ __ ___ ___ | | __ _
| '_ ` _ \ / _ \| |/ _` | Modular Optimization framework for
| | | | | | (_) | | (_| | Localization and mApping (MOLA)
|_| |_| |_|\___/|_|\__,_| https://github.com/MOLAorg/mola
Copyright (C) 2018-2026 Jose Luis Blanco, University of Almeria,
and individual contributors.
SPDX-License-Identifier: GPL-3.0
See LICENSE for full license information.
*/
#pragma once
#include <mola_metric_maps/FixedDenseGrid3D.h>
#include <mola_metric_maps/index3d_t.h>
#include <mrpt/core/round.h>
#include <mrpt/img/TColor.h>
#include <mrpt/img/color_maps.h>
#include <mrpt/maps/CMetricMap.h>
#include <mrpt/maps/CSimplePointsMap.h>
#include <mrpt/maps/NearestNeighborsCapable.h>
#include <mrpt/math/TBoundingBox.h>
#include <mrpt/math/TPoint3D.h>
#include <array>
#include <functional>
#include <map>
#include <optional>
#include <shared_mutex>
namespace mola
{
class SparseTreesPointCloud : public mrpt::maps::CMetricMap,
public mrpt::maps::NearestNeighborsCapable
{
DEFINE_SERIALIZABLE(SparseTreesPointCloud, mola)
public:
constexpr static uint8_t GLOBAL_ID_SUBVOXEL_BITCOUNT = 20;
using outer_index3d_t = index3d_t<int32_t>;
using inner_plain_index_t = uint32_t;
using global_plain_index_t = uint64_t;
inline outer_index3d_t coordToOuterIdx(const mrpt::math::TPoint3Df& pt) const
{
return outer_index3d_t(
static_cast<int32_t>((pt.x + grid_size_half_) * grid_size_inv_), //
static_cast<int32_t>((pt.y + grid_size_half_) * grid_size_inv_), //
static_cast<int32_t>((pt.z + grid_size_half_) * grid_size_inv_));
}
inline mrpt::math::TPoint3Df outerIdxToCoord(const outer_index3d_t idx) const
{
return {
(idx.cx - 0.5f) * grid_size_, //
(idx.cy - 0.5f) * grid_size_, //
(idx.cz - 0.5f) * grid_size_};
}
static inline global_plain_index_t g2plain(const outer_index3d_t& g, int subVoxelIndex = 0)
{
constexpr uint64_t SUBVOXEL_MASK = ((1 << GLOBAL_ID_SUBVOXEL_BITCOUNT) - 1);
constexpr auto OFF = GLOBAL_ID_SUBVOXEL_BITCOUNT;
constexpr int FBITS = (64 - OFF) / 3;
constexpr uint64_t FMASK = (1 << FBITS) - 1;
return (static_cast<uint64_t>(subVoxelIndex) & SUBVOXEL_MASK) |
(static_cast<uint64_t>(g.cx & FMASK) << (OFF + FBITS * 0)) |
(static_cast<uint64_t>(g.cy & FMASK) << (OFF + FBITS * 1)) |
(static_cast<uint64_t>(g.cz & FMASK) << (OFF + FBITS * 2));
}
SparseTreesPointCloud(float grid_size = 10.0f);
~SparseTreesPointCloud();
void setGridProperties(float grid_size);
struct GridData
{
public:
GridData() = default;
auto& points() { return points_; }
const auto& points() const { return points_; }
void insertPoint(const mrpt::math::TPoint3Df& p) { points_.insertPoint(p); }
private:
mrpt::maps::CSimplePointsMap points_;
};
using grids_map_t = std::map<outer_index3d_t, GridData, index3d_hash<int32_t>>;
// clear(): available in base class
inline GridData* gridByOuterIdxs(const outer_index3d_t& oIdx, bool createIfNew)
{
// 1) Insert into grid map:
GridData* grid = nullptr;
cachedMtx_.lock_shared();
for (int i = 0; i < CachedData::NUM_CACHED_IDXS; i++)
{
if (cached_.lastAccessGrid[i] && cached_.lastAccessIdx[i] == oIdx)
{
// Cache hit:
#ifdef USE_DEBUG_PROFILER
mrpt::system::CTimeLoggerEntry tle(profiler, "insertPoint.cache_hit");
#endif
grid = cached_.lastAccessGrid[i];
break;
}
}
cachedMtx_.unlock_shared();
if (!grid)
{
// Cache miss:
#ifdef USE_DEBUG_PROFILER
mrpt::system::CTimeLoggerEntry tle(profiler, "insertPoint.cache_misss");
#endif
auto it = grids_.find(oIdx);
if (it == grids_.end())
{
if (!createIfNew)
{
return nullptr;
}
grid = &grids_[oIdx]; // Create it
}
else
{
grid = &it->second; // Use the found grid
}
// Add to cache:
cachedMtx_.lock();
cached_.lastAccessIdx[cached_.lastAccessNextWrite] = oIdx;
cached_.lastAccessGrid[cached_.lastAccessNextWrite] = grid;
cached_.lastAccessNextWrite++;
cached_.lastAccessNextWrite &= CachedData::NUM_CACHED_IDX_MASK;
cachedMtx_.unlock();
}
return grid;
}
// const version:
inline const GridData* gridByOuterIdxs(const outer_index3d_t& oIdx, bool createIfNew) const
{ // reuse the non-const method:
return const_cast<SparseTreesPointCloud*>(this)->gridByOuterIdxs(oIdx, createIfNew);
}
void insertPoint(const mrpt::math::TPoint3Df& pt)
{
auto* g = gridByOuterIdxs(coordToOuterIdx(pt), true);
g->insertPoint(pt);
// Also, update bbox:
if (!cached_.boundingBox_.has_value())
{
cached_.boundingBox_.emplace(pt, pt);
}
else
{
cached_.boundingBox_->updateWithPoint(pt);
}
}
const grids_map_t& grids() const { return grids_; }
mrpt::math::TBoundingBoxf boundingBox() const override;
void visitAllPoints(const std::function<void(const mrpt::math::TPoint3Df&)>& f) const;
void visitAllGrids(const std::function<void(const outer_index3d_t&, const GridData&)>& f) const;
bool saveToTextFile(const std::string& file) const;
void eraseGridsFartherThan(const mrpt::math::TPoint3Df& pt, const float distanceMeters);
void nn_prepare_for_2d_queries() const override;
void nn_prepare_for_3d_queries() const override;
[[nodiscard]] bool nn_has_indices_or_ids() const override;
[[nodiscard]] size_t nn_index_count() const override;
[[nodiscard]] bool nn_single_search(
const mrpt::math::TPoint3Df& query, mrpt::math::TPoint3Df& result, float& out_dist_sqr,
uint64_t& resultIndexOrID) const override;
[[nodiscard]] bool nn_single_search(
const mrpt::math::TPoint2Df& query, mrpt::math::TPoint2Df& result, float& out_dist_sqr,
uint64_t& resultIndexOrID) const override;
void nn_multiple_search(
const mrpt::math::TPoint3Df& query, const size_t N,
std::vector<mrpt::math::TPoint3Df>& results, std::vector<float>& out_dists_sqr,
std::vector<uint64_t>& resultIndicesOrIDs) const override;
void nn_multiple_search(
const mrpt::math::TPoint2Df& query, const size_t N,
std::vector<mrpt::math::TPoint2Df>& results, std::vector<float>& out_dists_sqr,
std::vector<uint64_t>& resultIndicesOrIDs) const override;
void nn_radius_search(
const mrpt::math::TPoint3Df& query, const float search_radius_sqr,
std::vector<mrpt::math::TPoint3Df>& results, std::vector<float>& out_dists_sqr,
std::vector<uint64_t>& resultIndicesOrIDs, size_t maxPoints) const override;
void nn_radius_search(
const mrpt::math::TPoint2Df& query, const float search_radius_sqr,
std::vector<mrpt::math::TPoint2Df>& results, std::vector<float>& out_dists_sqr,
std::vector<uint64_t>& resultIndicesOrIDs, size_t maxPoints) const override;
std::string asString() const override;
void getVisualizationInto(mrpt::opengl::CSetOfObjects& outObj) const override;
bool isEmpty() const override;
void saveMetricMapRepresentationToFile(const std::string& filNamePrefix) const override;
struct TInsertionOptions : public mrpt::config::CLoadableOptions
{
TInsertionOptions() = default;
void loadFromConfigFile(
const mrpt::config::CConfigFileBase& source,
const std::string& section) override; // See base docs
void dumpToTextStream(std::ostream& out) const override; // See base docs
void writeToStream(mrpt::serialization::CArchive& out) const;
void readFromStream(mrpt::serialization::CArchive& in);
float minimum_points_clearance = 0.20f;
float remove_submaps_farther_than = .0f;
};
TInsertionOptions insertionOptions;
struct TLikelihoodOptions : public mrpt::config::CLoadableOptions
{
TLikelihoodOptions() = default;
void loadFromConfigFile(
const mrpt::config::CConfigFileBase& source,
const std::string& section) override; // See base docs
void dumpToTextStream(std::ostream& out) const override; // See base docs
void writeToStream(mrpt::serialization::CArchive& out) const;
void readFromStream(mrpt::serialization::CArchive& in);
double sigma_dist = 0.5;
double max_corr_distance = 1.0;
uint32_t decimation = 10;
};
TLikelihoodOptions likelihoodOptions;
struct TRenderOptions : public mrpt::config::CLoadableOptions
{
void loadFromConfigFile(
const mrpt::config::CConfigFileBase& source,
const std::string& section) override; // See base docs
void dumpToTextStream(std::ostream& out) const override; // See base docs
void writeToStream(mrpt::serialization::CArchive& out) const;
void readFromStream(mrpt::serialization::CArchive& in);
float point_size = 1.0f;
bool show_inner_grid_boxes = false;
mrpt::img::TColorf color{.0f, .0f, 1.0f};
mrpt::img::TColormap colormap = mrpt::img::cmHOT;
std::string recolorByPointField = "intensity";
};
TRenderOptions renderOptions;
public:
// Interface for use within a mrpt::maps::CMultiMetricMap:
MAP_DEFINITION_START(SparseTreesPointCloud)
float grid_size = 10.0f;
mola::SparseTreesPointCloud::TInsertionOptions insertionOpts;
mola::SparseTreesPointCloud::TLikelihoodOptions likelihoodOpts;
mola::SparseTreesPointCloud::TRenderOptions renderOpts;
MAP_DEFINITION_END(SparseTreesPointCloud)
private:
float grid_size_ = 10.0f;
// Calculated from the above, in setVoxelProperties()
float grid_size_inv_ = 1.0f / grid_size_;
float grid_size_half_ = 0.5f * grid_size_;
mrpt::math::TVector3D gridVector_;
grids_map_t grids_;
struct CachedData
{
CachedData() = default;
void reset() { *this = CachedData(); }
mutable std::optional<mrpt::math::TBoundingBoxf> boundingBox_;
// 2 bits seems to be the optimum for typical cases:
constexpr static int CBITS = 2;
constexpr static int NUM_CACHED_IDXS = 1 << CBITS;
constexpr static int NUM_CACHED_IDX_MASK = NUM_CACHED_IDXS - 1;
int lastAccessNextWrite = 0;
outer_index3d_t lastAccessIdx[NUM_CACHED_IDXS];
GridData* lastAccessGrid[NUM_CACHED_IDXS] = {nullptr};
};
CachedData cached_;
std::shared_mutex cachedMtx_;
protected:
// See docs in base CMetricMap class:
void internal_clear() override;
private:
// See docs in base CMetricMap class:
bool internal_insertObservation(
const mrpt::obs::CObservation& obs,
const std::optional<const mrpt::poses::CPose3D>& robotPose = std::nullopt) override;
// See docs in base class
double internal_computeObservationLikelihood(
const mrpt::obs::CObservation& obs, const mrpt::poses::CPose3D& takenFrom) const override;
double internal_computeObservationLikelihoodPointCloud3D(
const mrpt::poses::CPose3D& pc_in_map, const float* xs, const float* ys, const float* zs,
const std::size_t num_pts) const;
void internal_insertPointCloud3D(
const mrpt::poses::CPose3D& pc_in_map, const float* xs, const float* ys, const float* zs,
const std::size_t num_pts);
// See docs in base class
bool internal_canComputeObservationLikelihood(const mrpt::obs::CObservation& obs) const override;
};
} // namespace mola