NavMap
Loading...
Searching...
No Matches
NavMap.hpp
Go to the documentation of this file.
1// Copyright 2025 Intelligent Robotics Lab
2//
3// This file is part of the project Easy Navigation (EasyNav in short)
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#ifndef NAVMAP_CORE__NAVMAP_HPP_
17#define NAVMAP_CORE__NAVMAP_HPP_
18
35
36#include <vector>
37#include <memory>
38#include <unordered_map>
39#include <optional>
40#include <string>
41#include <array>
42#include <limits>
43#include <cmath>
44#include <cstdint>
45#include <type_traits>
46#include <deque>
47#include <algorithm>
48#include <utility>
49
50#include "Eigen/Core"
52
53namespace navmap
54{
55
57using PointId = uint32_t;
59using NavCelId = uint32_t;
60
61// -----------------------------------------------------------------------------
62// Core data arrays
63// -----------------------------------------------------------------------------
64
75{
76 std::vector<float> x;
77 std::vector<float> y;
78 std::vector<float> z;
79
81 inline size_t size() const {return x.size();}
82
89 inline Eigen::Vector3f at(PointId id) const
90 {
91 return {x[id], y[id], z[id]};
92 }
93};
94
100struct Colors
101{
102 std::vector<uint8_t> r;
103 std::vector<uint8_t> g;
104 std::vector<uint8_t> b;
105 std::vector<uint8_t> a;
106};
107
108// -----------------------------------------------------------------------------
109// Layers (dynamic per-NavCel attributes)
110// -----------------------------------------------------------------------------
111
117enum class LayerType : uint8_t { U8 = 0, F32 = 1, F64 = 2, I16 = 3 };
118
125{
126 virtual ~LayerViewBase() = default;
127
129 virtual LayerType type() const = 0;
130
132 virtual const std::string & name() const = 0;
133
135 virtual size_t size() const = 0;
136
138 virtual std::uint64_t content_hash() const = 0;
139
141 void mark_dirty() const noexcept {hash_dirty_ = true;}
142
144 virtual std::shared_ptr<LayerViewBase> clone() const = 0;
145
146 // ---- Change tracking (for incremental consumers: inflation, publication...) ----
147 // Cells written since the last clear_changes(): recorded by NavMap::layer_set(), OccupancyLayer
148 // and mark_changed(). Bulk writes (set_data, layer_clear, copies) mark the whole layer, and so
149 // does creating it. Direct writes through data() must call mark_changed() themselves.
150
152 void mark_changed(NavCelId cid) const
153 {
154 hash_dirty_ = true;
155 if (all_changed_) {return;}
156 const std::size_t word = cid / 64;
157 if (changed_bits_.size() <= word) {
158 changed_bits_.resize(std::max(word + 1, (size() + 63) / 64));
159 }
160 const std::uint64_t bit = std::uint64_t{1} << (cid % 64);
161 if (!(changed_bits_[word] & bit)) {
162 changed_bits_[word] |= bit;
163 changed_.push_back(cid);
164 }
165 }
166
168 void mark_all_changed() const noexcept {hash_dirty_ = true; all_changed_ = true;}
169
171 bool all_changed() const noexcept {return all_changed_;}
172
174 const std::vector<NavCelId> & changed() const noexcept {return changed_;}
175
176 bool has_changes() const noexcept {return all_changed_ || !changed_.empty();}
177
179 void clear_changes() const
180 {
181 for (const NavCelId c : changed_) {changed_bits_[c / 64] &= ~(std::uint64_t{1} << (c % 64));}
182 changed_.clear();
183 all_changed_ = false;
184 }
185
186protected:
187 mutable bool hash_dirty_{true};
188 mutable std::uint64_t hash_cache_{0};
189 mutable bool all_changed_{true}; // a new layer is all new
190 mutable std::vector<NavCelId> changed_;
191 mutable std::vector<std::uint64_t> changed_bits_; // allocated on the first single change
192};
193
200template<typename T>
201struct LayerView : LayerViewBase
202{
203 std::string name_;
204 std::vector<T> data_;
206
213 LayerView(std::string name, size_t nitems, LayerType t)
214 : name_(std::move(name)), data_(nitems), type_(t) {}
215
216 LayerType type() const override {return type_;}
217 const std::string & name() const override {return name_;}
218 size_t size() const override {return data_.size();}
219
222 T & operator[](NavCelId cid) {return data_[cid];}
223 const T & operator[](NavCelId cid) const {return data_[cid];}
225
227 std::vector<T> & data() {return data_;}
229 const std::vector<T> & data() const {return data_;}
230
233 std::vector<T> & mutable_data() const
234 {
235 hash_dirty_ = true; return const_cast<std::vector<T> &>(data_);
236 }
237 void set_data(const std::vector<T> & v) {data_ = v; mark_all_changed();}
238 std::uint64_t content_hash() const override;
239
240 std::shared_ptr<LayerViewBase> clone() const override
241 {
242 return std::make_shared<LayerView<T>>(*this);
243 }
244
245};
246
248namespace detail
249{
250inline std::uint64_t fnv1a64_bytes(
251 const void * data, std::size_t n,
252 std::uint64_t seed = 1469598103934665603ULL)
253{
254 const auto * p = static_cast<const std::uint8_t *>(data);
255 std::uint64_t h = seed;
256 for (std::size_t i = 0; i < n; ++i) {
257 h ^= p[i]; h *= 1099511628211ULL;
258 }
259 return h;
260}
261} // namespace detail
263
264template<typename T>
265std::uint64_t LayerView<T>::content_hash() const
266{
267 if (!hash_dirty_) {return hash_cache_;}
268 const std::size_t n = data_.size();
269 std::uint64_t h = navmap::detail::fnv1a64_bytes(&n, sizeof(n));
270 if (n) {
271 static_assert(
272 std::is_trivially_copyable<T>::value,
273 "LayerView<T> requires trivially copyable T.");
274 h = navmap::detail::fnv1a64_bytes(data_.data(), n * sizeof(T), h);
275 }
276 hash_cache_ = h;
277 hash_dirty_ = false;
278 return hash_cache_;
279}
280
289{
290public:
291 LayerRegistry() = default;
292 LayerRegistry(LayerRegistry &&) noexcept = default;
293 LayerRegistry & operator=(LayerRegistry &&) noexcept = default;
294
297 {
298 for (const auto & [name, view] : other.layers_) {
299 layers_.emplace(name, view ? view->clone() : nullptr);
300 }
301 }
302
304 {
305 if (this != &other) {
306 LayerRegistry tmp(other);
307 layers_.swap(tmp.layers_);
308 }
309 return *this;
310 }
311
324 template<typename T>
325 std::shared_ptr<LayerView<T>> add_or_get(
326 const std::string & name,
327 size_t nitems,
328 LayerType type)
329 {
330 auto it = layers_.find(name);
331 if (it != layers_.end()) {
332 return std::dynamic_pointer_cast<LayerView<T>>(it->second);
333 }
334 auto view = std::make_shared<LayerView<T>>(name, nitems, type);
335 layers_[name] = view;
336 return view;
337 }
338
344 std::shared_ptr<LayerViewBase> get(const std::string & name) const
345 {
346 auto it = layers_.find(name);
347 return it == layers_.end() ? nullptr : it->second;
348 }
349
354 std::vector<std::string> list() const
355 {
356 std::vector<std::string> out;
357 out.reserve(layers_.size());
358 for (const auto & kv : layers_) {
359 out.push_back(kv.first);
360 }
361 return out;
362 }
363
368 bool remove(const std::string & name)
369 {
370 return layers_.erase(name) > 0;
371 }
372
380 void resize_all(size_t nitems)
381 {
382 for (auto & kv : layers_) {
383 if (auto v = std::dynamic_pointer_cast<LayerView<uint8_t>>(kv.second)) {
384 v->data_.resize(nitems);
385 kv.second->mark_all_changed();
386 continue;
387 }
388 if (auto v = std::dynamic_pointer_cast<LayerView<float>>(kv.second)) {
389 v->data_.resize(nitems);
390 kv.second->mark_all_changed();
391 continue;
392 }
393 if (auto v = std::dynamic_pointer_cast<LayerView<double>>(kv.second)) {
394 v->data_.resize(nitems);
395 kv.second->mark_all_changed();
396 continue;
397 }
398 if (auto v = std::dynamic_pointer_cast<LayerView<int16_t>>(kv.second)) {
399 v->data_.resize(nitems);
400 kv.second->mark_all_changed();
401 continue;
402 }
403 }
404 }
405
406private:
407 std::unordered_map<std::string, std::shared_ptr<LayerViewBase>> layers_;
408};
409
416{
417 std::string description;
418 std::string unit;
419 bool per_cell{true};
420 std::optional<double> unknown;
421};
422
423// -----------------------------------------------------------------------------
424// Mesh cells and acceleration
425// -----------------------------------------------------------------------------
426
436struct NavCel
437{
438 PointId v[3]{0, 0, 0};
439 Eigen::Vector3f normal{0.0f, 0.0f, 1.0f};
440 float area{0.0f};
442 std::numeric_limits<uint32_t>::max(),
443 std::numeric_limits<uint32_t>::max(),
444 std::numeric_limits<uint32_t>::max()
445 };
446 uint32_t layer_dirty_mask{0};
447};
448
455{
457 int left{-1};
458 int right{-1};
459 int start{0};
460 int count{0};
461 bool is_leaf() const {return count > 0;}
462};
463
471{
472 std::string frame_id;
473 std::vector<NavCelId> navcels;
475 std::vector<int> prim_indices;
476 std::vector<BVHNode> bvh;
477};
478
479// -----------------------------------------------------------------------------
480// Rays
481// -----------------------------------------------------------------------------
482
488struct Ray
489{
490 Eigen::Vector3f o;
491 Eigen::Vector3f d;
492};
493
499struct RayHit
500{
501 bool hit{false};
502 size_t surface{0};
504 float t{0.0f};
505 Eigen::Vector3f p;
506};
507
508// -----------------------------------------------------------------------------
509// Helpers (type → LayerType mapping)
510// -----------------------------------------------------------------------------
511
513template<typename T> inline constexpr LayerType layer_type_tag();
514template<> inline constexpr LayerType layer_type_tag<uint8_t>() {return LayerType::U8;}
515template<> inline constexpr LayerType layer_type_tag<float>() {return LayerType::F32;}
516template<> inline constexpr LayerType layer_type_tag<double>() {return LayerType::F64;}
517template<> inline constexpr LayerType layer_type_tag<int16_t>() {return LayerType::I16;}
518
523
524// -----------------------------------------------------------------------------
525// NavMap: public API
526// -----------------------------------------------------------------------------
527
543enum class LinkType : uint8_t { GENERIC = 0, RAMP = 1, STAIRS = 2, ELEVATOR = 3, DOOR = 4 };
544
559
561{
562public:
564 std::optional<Colors> colors;
565 std::vector<NavCel> navcels;
566 std::vector<Surface> surfaces;
568 std::unordered_map<std::string, LayerMeta> layer_meta;
569
571 NavMap();
572
574 NavMap(const NavMap & other);
575
584
592 NavMap & operator=(const NavMap & other);
593
598 NavMap & operator=(NavMap && other) noexcept;
599
601 bool has_same_geometry(const NavMap & other) const;
602
610 std::uint64_t geometry_id() const
611 {
612 ensure_geometry_fingerprint_();
613 return geometry_fp_;
614 }
615
623 void build_adjacency();
624
626 std::size_t nonmanifold_edges() const {return nonmanifold_edges_;}
627
633
639 std::array<NavCelId, 3> get_neighbors(NavCelId cid) const
640 {
641 return {navcels[cid].neighbor[0],
642 navcels[cid].neighbor[1],
643 navcels[cid].neighbor[2]};
644 }
645
655 bool raycast(
656 const Eigen::Vector3f & o,
657 const Eigen::Vector3f & d,
658 NavCelId & hit_cid,
659 float & t,
660 Eigen::Vector3f & hit_pt) const;
661
668 void raycast_many(
669 const std::vector<Ray> & rays,
670 std::vector<RayHit> & out,
671 bool first_hit_only = true) const;
672
673 // --- Per-NavCel layer value access ---
674
682 template<typename T>
683 T navcel_value(NavCelId cid, const LayerView<T> & layer) const
684 {
685 return layer[cid];
686 }
687
698 {
699 std::optional<NavCelId> hint_cid;
700 std::optional<size_t> hint_surface;
701 float planar_eps = 1e-4f;
702 float height_eps = 0.50f;
703 bool use_downward_ray = true;
704 };
705
718 bool locate_navcel(
719 const Eigen::Vector3f & p_world,
720 size_t & surface_idx,
721 NavCelId & cid,
722 Eigen::Vector3f & bary,
723 Eigen::Vector3f * hit_pt) const;
724
742 const Eigen::Vector3f & p_world,
743 size_t & surface_idx,
744 NavCelId & cid,
745 Eigen::Vector3f & bary,
746 Eigen::Vector3f * hit_pt,
747 const LocateOpts & opts) const;
748
763 bool closest_navcel(
764 const Eigen::Vector3f & p_world,
765 size_t & surface_idx,
766 NavCelId & cid,
767 Eigen::Vector3f & closest_point,
768 float & sqdist,
769 int restrict_surface = -1) const;
770
771 // ---- Convenience: construction & editing (additive, backward-compatible) ----
772
776 std::size_t create_surface(std::string frame_id);
777
781 Surface create_surface_obj(const std::string & frame_id) const;
782
784 std::size_t add_surface(const Surface & s);
785
787 std::size_t add_surface(Surface && s);
788
792 bool remove_surface(std::size_t surface_index);
793
795 uint32_t add_vertex(const Eigen::Vector3f & p);
796
801 NavCelId add_navcel(uint32_t v0, uint32_t v1, uint32_t v2);
802
804 void add_navcel_to_surface(std::size_t surface_index, NavCelId cid);
805
807 Eigen::Vector3f navcel_centroid(NavCelId cid) const;
808
810 std::vector<NavCelId> navcel_neighbors(NavCelId cid) const;
811
812 // ---- Links between NavCels that are not adjacent (SurfaceLink) ----
813
815 static constexpr std::size_t kInvalidLink = std::numeric_limits<std::size_t>::max();
816
822 std::size_t add_link(
823 NavCelId from, NavCelId to, LinkType type = LinkType::GENERIC, float length = -1.0f,
824 bool bidirectional = true);
825
827 bool remove_link(std::size_t index);
828
829 void clear_links();
830
831 const std::vector<SurfaceLink> & links() const {return links_;}
832
834 const std::vector<std::uint32_t> & links_from(NavCelId cid) const;
835
849 std::vector<float> distance_field(
850 const std::vector<NavCelId> & seed_cells, bool from_boundary,
851 float max_distance = std::numeric_limits<float>::infinity()) const;
852
854 static constexpr std::size_t kNoSurface = std::numeric_limits<std::size_t>::max();
855
858 std::size_t surface_of(NavCelId cid) const;
859
860 // ---- Layers (per-NavCel) convenience ----
861
876 template<typename T>
877 std::shared_ptr<LayerView<T>> add_layer(
878 const std::string & name,
879 const std::string & description = {},
880 const std::string & unit = {},
881 T default_value = T{})
882 {
883 const bool existed = has_layer(name);
884 auto v = layers.add_or_get<T>(name, navcels.size(), layer_type_tag<T>());
885 if (!existed || v->data().size() != navcels.size()) { // new layers start at default_value
886 v->data().assign(navcels.size(), default_value);
887 v->mark_all_changed();
888 }
889 auto & meta = layer_meta[name];
890 meta.description = description;
891 meta.unit = unit;
892 meta.per_cell = true;
893 return v;
894 }
895
897 bool has_layer(const std::string & name) const;
898
900 std::size_t layer_size(const std::string & name) const;
901
903 std::string layer_type_name(const std::string & name) const;
904
912 template<typename T>
913 void layer_set(const std::string & name, NavCelId cid, T value)
914 {
915 auto view = layers.add_or_get<T>(name, navcels.size(), layer_type_tag<T>());
916 if (static_cast<size_t>(cid) < view->data().size()) {
917 view->data()[cid] = value;
918 view->mark_changed(cid);
919 }
920 }
921
937 template<typename T>
938 T layer_get(const std::string & name, NavCelId cid, T def = T{}) const
939 {
940 if (auto base = layers.get(name)) {
941 if (auto view = std::dynamic_pointer_cast<LayerView<T>>(base)) {
942 const auto & buf = view->data();
943 if (static_cast<size_t>(cid) < buf.size()) {
944 return buf[cid];
945 }
946 return def;
947 }
948 }
949 // Fallback: try to fetch as double from any stored type and convert
950 double v;
951 if (auto base = layers.get(name)) {
952 if (auto v8 = std::dynamic_pointer_cast<LayerView<uint8_t>>(base)) {
953 const auto & buf = v8->data();
954 if (static_cast<size_t>(cid) >= buf.size()) {return def;}
955 v = static_cast<double>(buf[cid]);
956 } else if (auto vf = std::dynamic_pointer_cast<LayerView<float>>(base)) {
957 const auto & buf = vf->data();
958 if (static_cast<size_t>(cid) >= buf.size()) {return def;}
959 v = static_cast<double>(buf[cid]);
960 } else if (auto vd = std::dynamic_pointer_cast<LayerView<double>>(base)) {
961 const auto & buf = vd->data();
962 if (static_cast<size_t>(cid) >= buf.size()) {return def;}
963 v = buf[cid];
964 } else if (auto vi = std::dynamic_pointer_cast<LayerView<int16_t>>(base)) {
965 const auto & buf = vi->data();
966 if (static_cast<size_t>(cid) >= buf.size()) {return def;}
967 v = static_cast<double>(buf[cid]);
968 } else {
969 return def;
970 }
971 } else {
972 return def;
973 }
974
975 if constexpr (std::is_floating_point_v<T>) {
976 return static_cast<T>(v);
977 } else if constexpr (std::is_integral_v<T>) {
978 const double lo = static_cast<double>(std::numeric_limits<T>::lowest());
979 const double hi = static_cast<double>(std::numeric_limits<T>::max());
980 double vv = v < lo ? lo : (v > hi ? hi : v);
981 return static_cast<T>(std::llround(vv));
982 } else {
983 static_assert(sizeof(T) == 0, "Unsupported layer type");
984 }
985 }
986
992 std::optional<LayerMeta> get_layer_meta(const std::string & name) const;
993
998 void set_layer_unknown(const std::string & name, double value);
999
1001 bool is_unknown(const std::string & name, NavCelId cid) const;
1002
1007 std::vector<std::string> list_layers() const;
1008
1018 double sample_layer_at(
1019 const std::string & name,
1020 const Eigen::Vector3f & p_world,
1021 double def = std::numeric_limits<double>::quiet_NaN()) const;
1022
1030 template<typename T>
1031 void layer_clear(const std::string & name, T value = T{})
1032 {
1033 auto view = layers.add_or_get<T>(name, navcels.size(), layer_type_tag<T>());
1034 std::fill(view->data().begin(), view->data().end(), value);
1035 view->mark_all_changed();
1036 }
1037
1048 template<typename T>
1049 bool layer_copy(const std::string & src, const std::string & dst)
1050 {
1051 // No-op if names are equal
1052 if (src == dst) {
1053 return true;
1054 }
1055
1056 // Get source
1057 auto src_base = layers.get(src);
1058 auto src_view = std::dynamic_pointer_cast<LayerView<T>>(src_base);
1059 if (!src_view) {
1060 return false; // source layer missing or wrong type
1061 }
1062
1063 // Ensure destination exists with correct length (navcels.size())
1064 auto dst_view = layers.add_or_get<T>(dst, navcels.size(), layer_type_tag<T>());
1065 if (!dst_view) {
1066 return false;
1067 }
1068
1069 // If sizes mismatch, something is wrong with geometry/layer size.
1070 // You can choose to resize here if your semantics allow it. For safety, fail:
1071 if (src_view->data().size() != dst_view->data().size()) {
1072 return false;
1073 }
1074
1075 // If both views already refer to the same underlying buffer, no work to do
1076 if (!src_view->data().empty() &&
1077 src_view->data().data() == dst_view->data().data())
1078 {
1079 return true;
1080 }
1081
1082 // Hash-based skip: O(1) if cached, O(n) only on first compute (then cached)
1083 const bool same_hash = (src_view->content_hash() == dst_view->content_hash());
1084 if (same_hash) {
1085 return true; // identical content → avoid copy
1086 }
1087
1088 // Copy only when different. set_data() marks hash as dirty for dst.
1089 dst_view->set_data(src_view->data());
1090 return true;
1091 }
1092
1109 template<typename T>
1110 std::shared_ptr<LayerView<T>> add_layer(
1111 const std::string & name,
1112 const std::string & description,
1113 const std::string & unit,
1114 const std::string & src_layer)
1115 {
1116 auto dst = layers.add_or_get<T>(name, navcels.size(), layer_type_tag<T>());
1117 if (dst->data().size() != navcels.size()) {
1118 dst->data().assign(navcels.size(), T{});
1119 }
1120 auto & meta = layer_meta[name];
1121 meta.description = description;
1122 meta.unit = unit;
1123 meta.per_cell = true;
1124
1125 if (!has_layer(src_layer)) {
1126 return dst;
1127 }
1128
1129 if (layer_copy<T>(src_layer, name)) {
1130 return dst;
1131 }
1132
1133 const std::size_t n = dst->size();
1134 for (NavCelId cid = 0; cid < n; ++cid) {
1135 double v = layer_get<double>(src_layer, cid, std::numeric_limits<double>::quiet_NaN());
1136 if (std::isnan(v)) {v = 0.0;}
1137
1138 if constexpr (std::is_floating_point_v<T>) {
1139 dst->data()[cid] = static_cast<T>(v);
1140 } else if constexpr (std::is_integral_v<T>) {
1141 const double lo = static_cast<double>(std::numeric_limits<T>::lowest());
1142 const double hi = static_cast<double>(std::numeric_limits<T>::max());
1143 if (v < lo) {v = lo;}
1144 if (v > hi) {v = hi;}
1145 dst->data()[cid] = static_cast<T>(std::llround(v));
1146 } else {
1147 static_assert(sizeof(T) == 0, "Unsupported layer type");
1148 }
1149 }
1150
1151 return dst;
1152 }
1153
1160 bool locate_navcel_core(
1161 const Eigen::Vector3f & p_world,
1162 std::size_t & surface_idx,
1163 NavCelId & cid,
1164 Eigen::Vector3f & bary,
1165 Eigen::Vector3f * hit_pt,
1166 const LocateOpts & opts) const;
1167
1185 template<typename T>
1187 const Eigen::Vector3f & p_world,
1188 T value,
1189 const std::string & layer_name,
1190 AreaShape shape,
1191 float size)
1192 {
1193 if (navcels.empty() || surfaces.empty() || size <= 0.0f) {
1194 return false;
1195 }
1196
1197 std::size_t surface_idx = 0;
1198 NavCelId start_cid{};
1199 Eigen::Vector3f bary{};
1200 Eigen::Vector3f hit_pt{};
1201 if (!locate_navcel(p_world, surface_idx, start_cid, bary, &hit_pt)) {
1202 return false;
1203 }
1204
1205 const float cx = hit_pt.x();
1206 const float cy = hit_pt.y();
1207
1208 {
1209 auto existing = layers.get(layer_name);
1210 if (existing && existing->type() != layer_type_tag<T>()) {
1211 return false; // type mismatch
1212 }
1213 }
1214 auto layer = layers.add_or_get<T>(layer_name, navcels.size(), layer_type_tag<T>());
1215 if (!layer) {
1216 return false;
1217 }
1218 if (layer->data().size() != navcels.size()) {
1219 const_cast<std::vector<T> &>(layer->data()).resize(navcels.size(), T{});
1220 }
1221
1222 const float half = (shape == AreaShape::CIRCULAR) ? size : (0.5f * size);
1223 const float minx = cx - half, maxx = cx + half;
1224 const float miny = cy - half, maxy = cy + half;
1225 const float r2 = (shape == AreaShape::CIRCULAR) ? (size * size) : 0.0f;
1226
1227 auto tri_aabb_intersects_box = [&](NavCelId cid) -> bool {
1228 const auto & tri = navcels[static_cast<std::size_t>(cid)];
1229 const Eigen::Vector3f p0{positions.x[tri.v[0]], positions.y[tri.v[0]],
1230 positions.z[tri.v[0]]};
1231 const Eigen::Vector3f p1{positions.x[tri.v[1]], positions.y[tri.v[1]],
1232 positions.z[tri.v[1]]};
1233 const Eigen::Vector3f p2{positions.x[tri.v[2]], positions.y[tri.v[2]],
1234 positions.z[tri.v[2]]};
1235 const float tminx = std::min({p0.x(), p1.x(), p2.x()});
1236 const float tmaxx = std::max({p0.x(), p1.x(), p2.x()});
1237 const float tminy = std::min({p0.y(), p1.y(), p2.y()});
1238 const float tmaxy = std::max({p0.y(), p1.y(), p2.y()});
1239 if (tmaxx < minx || tminx > maxx || tmaxy < miny || tminy > maxy) {
1240 return false;
1241 }
1242 return true;
1243 };
1244
1245 auto inside_area = [&](const Eigen::Vector3f & c) -> bool {
1246 const float dx = c.x() - cx;
1247 const float dy = c.y() - cy;
1248 if (shape == AreaShape::CIRCULAR) {
1249 return (dx * dx + dy * dy) <= r2;
1250 } else {
1251 return (std::abs(dx) <= half) && (std::abs(dy) <= half);
1252 }
1253 };
1254
1255 std::vector<char> visited(navcels.size(), 0);
1256 std::deque<NavCelId> q;
1257 q.push_back(start_cid);
1258 visited[static_cast<std::size_t>(start_cid)] = 1;
1259
1260 auto & data = layer->mutable_data();
1261
1262 while (!q.empty()) {
1263 const NavCelId cid = q.front(); q.pop_front();
1264
1265 if (!tri_aabb_intersects_box(cid)) {
1266 continue;
1267 }
1268
1269 const Eigen::Vector3f c = navcel_centroid(cid);
1270 if (inside_area(c)) {
1271 data[static_cast<std::size_t>(cid)] = value;
1272 }
1273
1274 for (const auto ncid : navcel_neighbors(cid)) {
1275 const std::size_t nidx = static_cast<std::size_t>(ncid);
1276 if (nidx < navcels.size() && !visited[nidx] && tri_aabb_intersects_box(ncid)) {
1277 visited[nidx] = 1;
1278 q.push_back(ncid);
1279 }
1280 }
1281 }
1282
1283 return true;
1284 }
1285
1286private:
1287 // Geometry fingerprint cache (lazy recompute)
1288 void ensure_geometry_fingerprint_() const;
1289 static std::uint64_t hash_geometry_bytes_(
1290 const float * x, std::size_t nx,
1291 const float * y, std::size_t ny,
1292 const float * z, std::size_t nz,
1293 const std::uint32_t * v0, std::size_t nv0,
1294 const std::uint32_t * v1, std::size_t nv1,
1295 const std::uint32_t * v2, std::size_t nv2);
1296
1297 mutable bool geometry_dirty_{true};
1298 mutable std::uint64_t geometry_fp_{0};
1299
1300 // Closest hit over all surfaces with t < t_max, nearest surfaces first.
1301 bool closest_hit_(
1302 const Eigen::Vector3f & o, const Eigen::Vector3f & d, float t_max,
1303 RayHit & out) const;
1304
1305 std::size_t nonmanifold_edges_{0};
1306
1307 // Owning surface per NavCel, built in rebuild_geometry_accels().
1308 std::vector<std::uint32_t> navcel_surface_;
1309
1310 // BVH over the surfaces' boxes (leaves index surface_tree_prims_), built in
1311 // rebuild_geometry_accels(); queries visit only the surfaces it reaches. Stale (surfaces added
1312 // since) means the queries go through every surface, as without it.
1313 std::vector<BVHNode> surface_tree_;
1314 std::vector<std::uint32_t> surface_tree_prims_;
1315 std::size_t surface_tree_surfaces_{0};
1316 void build_surface_tree_();
1317 bool surface_tree_valid_() const
1318 {
1319 return !surface_tree_.empty() && surface_tree_surfaces_ == surfaces.size();
1320 }
1321 std::vector<SurfaceLink> links_;
1322 mutable std::vector<std::vector<std::uint32_t>> link_index_;
1323 mutable bool link_index_dirty_{true};
1324 void build_navcel_surface_index_();
1325
1326 // Builders and traversal helpers.
1327
1332 void build_surface_bvh(Surface & s);
1333
1345 bool surface_raycast(
1346 const Surface & s,
1347 const Eigen::Vector3f & o,
1348 const Eigen::Vector3f & d,
1349 NavCelId & hit_cid,
1350 float & t_out,
1351 Eigen::Vector3f & hit_pt,
1352 float t_max = std::numeric_limits<float>::infinity()) const;
1353
1364 bool locate_by_walking(
1365 NavCelId start_cid,
1366 const Eigen::Vector3f & p,
1367 NavCelId & cid_out,
1368 Eigen::Vector3f & bary,
1369 Eigen::Vector3f * hit_pt,
1370 float planar_eps) const;
1371
1382 bool surface_closest_navcel(
1383 const Surface & s,
1384 const Eigen::Vector3f & p,
1385 NavCelId & cid,
1386 Eigen::Vector3f & q,
1387 float & best_sq) const;
1388};
1389
1390// Inline convenience overload.
1392 const Eigen::Vector3f & p_world,
1393 size_t & surface_idx,
1394 NavCelId & cid,
1395 Eigen::Vector3f & bary,
1396 Eigen::Vector3f * hit_pt) const
1397{
1398 return locate_navcel(p_world, surface_idx, cid, bary, hit_pt, LocateOpts{});
1399}
1400
1401} // namespace navmap
1402
1403#endif // NAVMAP_CORE__NAVMAP_HPP_
Low-level geometry utilities for NavMap cells (triangles), AABB and basic queries such as ray-triangl...
NavMap()
Copy constructor: deep copy of geometry, surfaces, layers and metadata.
Definition NavMap.cpp:49
Registry of named layers (per-NavCel).
Definition NavMap.hpp:289
bool remove(const std::string &name)
Remove a layer by name.
Definition NavMap.hpp:368
void resize_all(size_t nitems)
Resize all known typed layers to nitems.
Definition NavMap.hpp:380
std::shared_ptr< LayerViewBase > get(const std::string &name) const
Get an existing layer by name (untyped view).
Definition NavMap.hpp:344
std::vector< std::string > list() const
List layer names currently in the registry.
Definition NavMap.hpp:354
std::shared_ptr< LayerView< T > > add_or_get(const std::string &name, size_t nitems, LayerType type)
Add a new typed layer or return an existing one with the same name.
Definition NavMap.hpp:325
LayerRegistry & operator=(const LayerRegistry &other)
Definition NavMap.hpp:303
LayerRegistry(LayerRegistry &&) noexcept=default
std::array< NavCelId, 3 > get_neighbors(NavCelId cid) const
Return the three neighbor NavCel ids of triangle cid.
Definition NavMap.hpp:639
Surface create_surface_obj(const std::string &frame_id) const
Create a standalone Surface object (not yet added to the map).
Definition NavMap.cpp:1019
std::uint64_t geometry_id() const
Fingerprint of the geometry (vertex positions and triangles), 64-bit FNV-1a.
Definition NavMap.hpp:610
void raycast_many(const std::vector< Ray > &rays, std::vector< RayHit > &out, bool first_hit_only=true) const
Batched raycast.
Definition NavMap.cpp:634
void add_navcel_to_surface(std::size_t surface_index, NavCelId cid)
Add an existing nav cell id to a surface.
Definition NavMap.cpp:1062
Eigen::Vector3f navcel_centroid(NavCelId cid) const
Triangle centroid (computed on the fly).
Definition NavMap.cpp:1069
bool layer_copy(const std::string &src, const std::string &dst)
Copy values from one layer into another.
Definition NavMap.hpp:1049
LayerRegistry layers
Per-NavCel layers (runtime registry).
Definition NavMap.hpp:567
std::vector< NavCel > navcels
All triangles (global indexing).
Definition NavMap.hpp:565
NavCelId add_navcel(uint32_t v0, uint32_t v1, uint32_t v2)
Append a triangle (NavCel) and return its id.
Definition NavMap.cpp:1054
bool is_unknown(const std::string &name, NavCelId cid) const
Whether layer name holds its unknown value at cid (false if it has none).
Definition NavMap.cpp:1358
bool remove_surface(std::size_t surface_index)
Remove a Surface by index.
Definition NavMap.cpp:1038
bool locate_navcel_core(const Eigen::Vector3f &p_world, std::size_t &surface_idx, NavCelId &cid, Eigen::Vector3f &bary, Eigen::Vector3f *hit_pt, const LocateOpts &opts) const
Forward to the existing low-level locator (implemented in .cpp).
Definition NavMap.cpp:754
std::optional< Colors > colors
Optional per-vertex colors.
Definition NavMap.hpp:564
std::shared_ptr< LayerView< T > > add_layer(const std::string &name, const std::string &description, const std::string &unit, const std::string &src_layer)
Create (or get) a typed per-NavCel layer, initialized from another layer.
Definition NavMap.hpp:1110
std::size_t surface_of(NavCelId cid) const
Index of the surface owning cid, or kNoSurface.
Definition NavMap.cpp:1103
void clear_links()
Definition NavMap.cpp:1299
bool locate_navcel(const Eigen::Vector3f &p_world, size_t &surface_idx, NavCelId &cid, Eigen::Vector3f &bary, Eigen::Vector3f *hit_pt) const
Locate the triangle under / near a world point (convenience).
Definition NavMap.hpp:1391
std::string layer_type_name(const std::string &name) const
Human-readable type name for a layer ("float", "double", "uint8", ...).
Definition NavMap.cpp:1332
std::size_t add_surface(const Surface &s)
Append an existing Surface (by value).
Definition NavMap.cpp:1026
bool has_same_geometry(const NavMap &other) const
Fast check for identical geometry (vertices and NavCel indices).
Definition NavMap.cpp:111
double sample_layer_at(const std::string &name, const Eigen::Vector3f &p_world, double def=std::numeric_limits< double >::quiet_NaN()) const
Sample a per-NavCel layer at a world position.
Definition NavMap.cpp:1373
void layer_clear(const std::string &name, T value=T{})
Reset all values in a layer to a given value.
Definition NavMap.hpp:1031
std::vector< NavCelId > navcel_neighbors(NavCelId cid) const
Return up to 3 neighbor cell ids (skips invalid entries).
Definition NavMap.cpp:1078
std::size_t nonmanifold_edges() const
Edges shared by more than two NavCels in the last adjacency build.
Definition NavMap.hpp:626
bool set_area(const Eigen::Vector3f &p_world, T value, const std::string &layer_name, AreaShape shape, float size)
Set a per-NavCel layer to a constant value over a 2D area.
Definition NavMap.hpp:1186
uint32_t add_vertex(const Eigen::Vector3f &p)
Append a vertex and return its index.
Definition NavMap.cpp:1045
std::vector< Surface > surfaces
Surfaces (partitions of navcels).
Definition NavMap.hpp:566
const std::vector< SurfaceLink > & links() const
Definition NavMap.hpp:831
std::size_t add_link(NavCelId from, NavCelId to, LinkType type=LinkType::GENERIC, float length=-1.0f, bool bidirectional=true)
Add a link from from to to.
Definition NavMap.cpp:1280
std::shared_ptr< LayerView< T > > add_layer(const std::string &name, const std::string &description={}, const std::string &unit={}, T default_value=T{})
Create (or get) a typed per-NavCel layer with a default value.
Definition NavMap.hpp:877
bool closest_navcel(const Eigen::Vector3f &p_world, size_t &surface_idx, NavCelId &cid, Eigen::Vector3f &closest_point, float &sqdist, int restrict_surface=-1) const
Find the closest triangle to a point.
Definition NavMap.cpp:916
void mark_vertex_updated(PointId)
Mark a vertex as updated (reserved for future cache invalidation).
Definition NavMap.hpp:632
std::vector< float > distance_field(const std::vector< NavCelId > &seed_cells, bool from_boundary, float max_distance=std::numeric_limits< float >::infinity()) const
Distance (m, 3D) from each NavCel centroid to the nearest seed.
Definition NavMap.cpp:1218
void set_layer_unknown(const std::string &name, double value)
Declare the value of layer name that means "not observed" (NaN allowed).
Definition NavMap.cpp:1353
T navcel_value(NavCelId cid, const LayerView< T > &layer) const
Read the value of a typed per-NavCel layer at triangle cid.
Definition NavMap.hpp:683
static constexpr std::size_t kInvalidLink
Value returned by add_link() when a NavCel id is out of range.
Definition NavMap.hpp:815
std::vector< std::string > list_layers() const
List all layer names currently stored.
Definition NavMap.cpp:1368
std::optional< LayerMeta > get_layer_meta(const std::string &name) const
Return metadata for a layer if present (implementation-defined).
Definition NavMap.cpp:1346
NavMap & operator=(const NavMap &other)
Copy assignment optimized to avoid geometry duplication.
Definition NavMap.cpp:127
bool locate_navcel(const Eigen::Vector3f &p_world, size_t &surface_idx, NavCelId &cid, Eigen::Vector3f &bary, Eigen::Vector3f *hit_pt, const LocateOpts &opts) const
Locate the triangle under / near a world point.
Positions positions
Vertex positions (SoA).
Definition NavMap.hpp:563
void layer_set(const std::string &name, NavCelId cid, T value)
Set a per-cell value (creates/retypes the layer if needed).
Definition NavMap.hpp:913
std::size_t create_surface(std::string frame_id)
Create a new empty Surface and append it to the map.
Definition NavMap.cpp:1012
const std::vector< std::uint32_t > & links_from(NavCelId cid) const
Indices of the links that can be taken from cid (both ends of bidirectional ones).
Definition NavMap.cpp:1305
static constexpr std::size_t kNoSurface
Value returned by surface_of() for a NavCel in no surface.
Definition NavMap.hpp:854
void build_adjacency()
Build topological adjacency between neighboring NavCels.
Definition NavMap.cpp:251
bool has_layer(const std::string &name) const
Check whether a layer named name exists.
Definition NavMap.cpp:1321
NavMap()
Copy constructor: deep copy of geometry, surfaces, layers and metadata.
Definition NavMap.cpp:49
std::size_t layer_size(const std::string &name) const
Number of entries in a layer (should equal navcels.size()).
Definition NavMap.cpp:1326
bool raycast(const Eigen::Vector3f &o, const Eigen::Vector3f &d, NavCelId &hit_cid, float &t, Eigen::Vector3f &hit_pt) const
Raycast against all surfaces to find the closest hit.
Definition NavMap.cpp:562
void rebuild_geometry_accels()
Recompute derived geometry and acceleration structures.
Definition NavMap.cpp:440
bool remove_link(std::size_t index)
Remove link index (the last link takes its index). False if out of range.
Definition NavMap.cpp:1290
T layer_get(const std::string &name, NavCelId cid, T def=T{}) const
Get a per-cell value as the requested type.
Definition NavMap.hpp:938
std::unordered_map< std::string, LayerMeta > layer_meta
Optional metadata per layer.
Definition NavMap.hpp:568
Definition Combine.hpp:39
LayerType
Runtime type tag for a layer's scalar storage.
Definition NavMap.hpp:117
@ F64
Definition NavMap.hpp:117
@ F32
Definition NavMap.hpp:117
@ U8
Definition NavMap.hpp:117
@ I16
Definition NavMap.hpp:117
AreaShape
Shape selector for area-writing APIs.
Definition NavMap.hpp:522
@ RECTANGULAR
Definition NavMap.hpp:522
@ CIRCULAR
Definition NavMap.hpp:522
LinkType
Main container for navigable surfaces, geometry, and layers.
Definition NavMap.hpp:543
@ ELEVATOR
Definition NavMap.hpp:543
@ STAIRS
Definition NavMap.hpp:543
@ DOOR
Definition NavMap.hpp:543
@ GENERIC
Definition NavMap.hpp:543
@ RAMP
Definition NavMap.hpp:543
constexpr LayerType layer_type_tag()
Helper to map C++ scalar type to navmap::LayerType tag.
uint32_t NavCelId
Index of a triangle (NavCel) within the global mesh.
Definition NavMap.hpp:59
uint32_t PointId
Index into the per-vertex position arrays (SoA).
Definition NavMap.hpp:57
LayerView(std::string name, size_t nitems, LayerType t)
Construct a typed view.
Definition NavMap.hpp:213
Options for the locate functions.
Definition NavMap.hpp:698
Axis-aligned bounding box.
Definition Geometry.hpp:165
Node in a per-surface bounding volume hierarchy (BVH).
Definition NavMap.hpp:455
int right
Right child index (or -1 for leaf).
Definition NavMap.hpp:458
int start
Start index in primitive array (leaf only).
Definition NavMap.hpp:459
bool is_leaf() const
Definition NavMap.hpp:461
int count
Number of primitives in leaf (0 for inner nodes).
Definition NavMap.hpp:460
int left
Left child index (or -1 for leaf).
Definition NavMap.hpp:457
AABB box
Bounding box of this node (world coordinates).
Definition NavMap.hpp:456
Optional per-vertex colors (RGBA, 8-bit per channel).
Definition NavMap.hpp:101
std::vector< uint8_t > r
Red channel.
Definition NavMap.hpp:102
std::vector< uint8_t > a
Alpha channel.
Definition NavMap.hpp:105
std::vector< uint8_t > g
Green channel.
Definition NavMap.hpp:103
std::vector< uint8_t > b
Blue channel.
Definition NavMap.hpp:104
Metadata associated to a layer (optional).
Definition NavMap.hpp:416
std::string unit
Physical unit (e.g., "m", "deg", "%").
Definition NavMap.hpp:418
std::string description
Human-readable description.
Definition NavMap.hpp:417
std::optional< double > unknown
Value meaning "not observed" (NaN allowed), if any.
Definition NavMap.hpp:420
bool per_cell
true for per-NavCel layers (current behavior)
Definition NavMap.hpp:419
Non-templated base for runtime layer handling.
Definition NavMap.hpp:125
void mark_all_changed() const noexcept
Record that the whole layer changed.
Definition NavMap.hpp:168
bool all_changed() const noexcept
Whether the whole layer changed (then changed() is not meaningful).
Definition NavMap.hpp:171
std::vector< NavCelId > changed_
Definition NavMap.hpp:190
virtual LayerType type() const =0
virtual const std::string & name() const =0
virtual std::uint64_t content_hash() const =0
Return 64-bit content hash (cached; recomputed lazily).
const std::vector< NavCelId > & changed() const noexcept
Cells changed one by one since the last clear_changes(), in order of first change.
Definition NavMap.hpp:174
std::vector< std::uint64_t > changed_bits_
Definition NavMap.hpp:191
std::uint64_t hash_cache_
Definition NavMap.hpp:188
void clear_changes() const
Forget the recorded changes (call it once every consumer has seen them).
Definition NavMap.hpp:179
void mark_dirty() const noexcept
Mark content dirty (forces hash recompute on next query).
Definition NavMap.hpp:141
virtual std::shared_ptr< LayerViewBase > clone() const =0
Independent copy of this layer (data, name, type, cached hash and pending changes).
virtual size_t size() const =0
virtual ~LayerViewBase()=default
bool all_changed_
Definition NavMap.hpp:189
bool hash_dirty_
Definition NavMap.hpp:187
bool has_changes() const noexcept
Definition NavMap.hpp:176
void mark_changed(NavCelId cid) const
Record that cell cid changed (deduplicated).
Definition NavMap.hpp:152
Typed layer view storing one T value per NavCel.
Definition NavMap.hpp:202
std::uint64_t content_hash() const override
Return 64-bit content hash (cached; recomputed lazily).
Definition NavMap.hpp:265
const std::vector< T > & data() const
Definition NavMap.hpp:229
void set_data(const std::vector< T > &v)
Definition NavMap.hpp:237
std::string name_
Layer name (key in the registry).
Definition NavMap.hpp:203
std::vector< T > data_
Values, one per NavCel.
Definition NavMap.hpp:204
LayerType type_
Runtime type tag (must match T).
Definition NavMap.hpp:205
LayerType type() const override
Definition NavMap.hpp:216
const std::string & name() const override
Definition NavMap.hpp:217
const T & operator[](NavCelId cid) const
Definition NavMap.hpp:223
std::vector< T > & mutable_data() const
Definition NavMap.hpp:233
T & operator[](NavCelId cid)
Definition NavMap.hpp:222
size_t size() const override
Definition NavMap.hpp:218
LayerView(std::string name, size_t nitems, LayerType t)
Construct a typed view.
Definition NavMap.hpp:213
std::vector< T > & data()
Definition NavMap.hpp:227
std::shared_ptr< LayerViewBase > clone() const override
Independent copy of this layer (data, name, type, cached hash and pending changes).
Definition NavMap.hpp:240
Navigation cell (triangle) with geometry and adjacency.
Definition NavMap.hpp:437
uint32_t layer_dirty_mask
Reserved for future per-layer flags.
Definition NavMap.hpp:446
Eigen::Vector3f normal
Unit normal of the triangle.
Definition NavMap.hpp:439
NavCelId neighbor[3]
Neighbor cids across edges 0,1,2.
Definition NavMap.hpp:441
float area
Triangle area (m²).
Definition NavMap.hpp:440
PointId v[3]
Indices into navmap::Positions.
Definition NavMap.hpp:438
Options for the locate functions.
Definition NavMap.hpp:698
std::optional< NavCelId > hint_cid
Optional triangle hint for walking.
Definition NavMap.hpp:699
std::optional< size_t > hint_surface
Optional surface hint.
Definition NavMap.hpp:700
bool use_downward_ray
Downward ray on fallback (else upward).
Definition NavMap.hpp:703
float height_eps
Z tolerance for vertical fallback (meters).
Definition NavMap.hpp:702
float planar_eps
In-plane barycentric tolerance.
Definition NavMap.hpp:701
Structure-of-arrays for storing 3D vertex positions.
Definition NavMap.hpp:75
Eigen::Vector3f at(PointId id) const
Returns vertex id as a 3D vector.
Definition NavMap.hpp:89
size_t size() const
Definition NavMap.hpp:81
std::vector< float > x
X coordinates (meters).
Definition NavMap.hpp:76
std::vector< float > z
Z coordinates (meters).
Definition NavMap.hpp:78
std::vector< float > y
Y coordinates (meters).
Definition NavMap.hpp:77
Result of a raycast against the NavMap.
Definition NavMap.hpp:500
Eigen::Vector3f p
World coordinates of intersection.
Definition NavMap.hpp:505
bool hit
True if the ray hit any triangle.
Definition NavMap.hpp:501
size_t surface
Index of surface hit.
Definition NavMap.hpp:502
NavCelId cid
Id of the NavCel hit.
Definition NavMap.hpp:503
float t
Distance along the ray (units of d).
Definition NavMap.hpp:504
Simple ray (origin + direction).
Definition NavMap.hpp:489
Eigen::Vector3f o
Origin of the ray (world).
Definition NavMap.hpp:490
Eigen::Vector3f d
Direction of the ray (unit length recommended).
Definition NavMap.hpp:491
A connected set of NavCels in a common reference frame.
Definition NavMap.hpp:471
std::vector< BVHNode > bvh
BVH nodes for this surface.
Definition NavMap.hpp:476
AABB aabb
Bounds of the surface geometry.
Definition NavMap.hpp:474
std::vector< NavCelId > navcels
NavCels belonging to this surface (global ids).
Definition NavMap.hpp:473
std::string frame_id
Frame id of this surface.
Definition NavMap.hpp:472
std::vector< int > prim_indices
Compact list of cids used by BVH leaves.
Definition NavMap.hpp:475