UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
ray_caster.hpp
1
42#ifndef UFO_MAP_RAY_CASTER_HPP
43#define UFO_MAP_RAY_CASTER_HPP
44
45// UFO
46#include <ufo/map/integration/integration_grid.hpp>
47#include <ufo/map/octree/octree_code.hpp>
48#include <ufo/map/octree/octree_key.hpp>
49#include <ufo/map/quadtree/quadtree_code.hpp>
50#include <ufo/map/quadtree/quadtree_key.hpp>
51#include <ufo/map/types.hpp>
52#include <ufo/numeric/util.hpp>
53#include <ufo/numeric/vec2.hpp>
54#include <ufo/numeric/vec3.hpp>
55
56// STL
57#include <cassert>
58#include <cstdint>
59#include <limits>
60#include <string>
61#include <unordered_map>
62#include <vector>
63
64namespace ufo
65{
66inline void computeRay(std::unordered_map<OctCode, IntegrationGrid<OctCode>>& misses,
67 Vec3f origin, Vec3f goal, OctCode c_origin, Vec3f voxel_border,
68 float grid_size, float max_distance, unsigned inflate_unknown)
69{
70 static constexpr auto max = std::numeric_limits<float>::max();
71 depth_t depth = c_origin.depth();
72
73 auto dir = goal - origin;
74 auto distance = dir.norm();
75 dir /= distance;
76
77 distance = std::min(distance, max_distance);
78
79 std::array<int, 3> sign{sgn(dir.x), sgn(dir.y), sgn(dir.z)};
80
81 if (0 == sign[0] && 0 == sign[1] && 0 == sign[2]) {
82 return;
83 }
84
85 std::array<void (OctCode ::*)(code_t), 3> step_f{
86 0 <= sign[0] ? (void (OctCode ::*)(code_t))(&OctCode::incX)
87 : (void (OctCode ::*)(code_t))(&OctCode::decX),
88 0 <= sign[1] ? (void (OctCode ::*)(code_t))(&OctCode::incY)
89 : (void (OctCode ::*)(code_t))(&OctCode::decY),
90 0 <= sign[2] ? (void (OctCode ::*)(code_t))(&OctCode::incZ)
91 : (void (OctCode ::*)(code_t))(&OctCode::decZ)};
92 std::array<code_t, 3> step_v{c_origin.xStep(), c_origin.yStep(), c_origin.zStep()};
93
94 Vec3f t_max(sign[0] ? (voxel_border.x + sign[0] * grid_size / 2.0f) / dir.x : max,
95 sign[1] ? (voxel_border.y + sign[1] * grid_size / 2.0f) / dir.y : max,
96 sign[2] ? (voxel_border.z + sign[2] * grid_size / 2.0f) / dir.z : max);
97
98 Vec3f t_delta(sign[0] ? grid_size / std::abs(dir.x) : max,
99 sign[1] ? grid_size / std::abs(dir.y) : max,
100 sign[2] ? grid_size / std::abs(dir.z) : max);
101
102 // FIXME: Is this correct? Should it be zero if all zero?
103 unsigned steps = (sign[0] ? std::ceil((distance - t_max[0]) / t_delta[0]) : 0) +
104 (sign[1] ? std::ceil((distance - t_max[1]) / t_delta[1]) : 0) +
105 (sign[2] ? std::ceil((distance - t_max[2]) / t_delta[2]) : 0);
106
107 OctCode prev_at_depth = c_origin.toDepth(IntegrationGrid<OctCode>::depth() + depth);
108 IntegrationGrid<OctCode>* misses_grid = &misses[prev_at_depth];
109 misses_grid->set(c_origin);
110
111 while (steps--) {
112 auto const advance_dim = t_max.minElementIndex();
113 (c_origin.*step_f[advance_dim])(step_v[advance_dim]);
114 t_max[advance_dim] += t_delta[advance_dim];
115
116 if (!OctCode::equalAtDepth(prev_at_depth, c_origin,
117 IntegrationGrid<OctCode>::depth() + depth)) {
118 prev_at_depth = c_origin.toDepth(IntegrationGrid<OctCode>::depth() + depth);
119 misses_grid = &misses[prev_at_depth];
120 }
121 misses_grid->set(c_origin);
122 }
123
124 // TODO: Can optimize this by calculating how many extra steps are needed above
125
126 unsigned advance_dim{};
127 std::array<unsigned, 3> s{};
128 while (s[advance_dim] != inflate_unknown) {
129 advance_dim = t_max.minElementIndex();
130 (c_origin.*step_f[advance_dim])(step_v[advance_dim]);
131 t_max[advance_dim] += t_delta[advance_dim];
132 ++s[advance_dim];
133
134 if (!OctCode::equalAtDepth(prev_at_depth, c_origin,
135 IntegrationGrid<OctCode>::depth() + depth)) {
136 prev_at_depth = c_origin.toDepth(IntegrationGrid<OctCode>::depth() + depth);
137 misses_grid = &misses[prev_at_depth];
138 }
139 misses_grid->set(c_origin);
140 }
141}
142
143inline void computeRay(std::unordered_map<OctCode, IntegrationGrid<OctCode>>& misses,
144 std::unordered_map<OctCode, IntegrationGrid<OctCode>> const& hits,
145 Vec3f origin, Vec3f goal, OctCode c_origin, Vec3f voxel_border,
146 float grid_size, float max_distance, unsigned inflate_unknown,
147 bool ray_passthrough_hits)
148{
149 static constexpr auto max = std::numeric_limits<float>::max();
150 depth_t depth = c_origin.depth();
151
152 auto dir = goal - origin;
153 auto distance = dir.norm();
154 dir /= distance;
155
156 distance = std::min(distance, max_distance);
157
158 std::array<int, 3> sign{sgn(dir.x), sgn(dir.y), sgn(dir.z)};
159
160 if (0 == sign[0] && 0 == sign[1] && 0 == sign[2]) {
161 return;
162 }
163
164 std::array<void (OctCode ::*)(code_t), 3> step_f{
165 0 <= sign[0] ? (void (OctCode ::*)(code_t))(&OctCode::incX)
166 : (void (OctCode ::*)(code_t))(&OctCode::decX),
167 0 <= sign[1] ? (void (OctCode ::*)(code_t))(&OctCode::incY)
168 : (void (OctCode ::*)(code_t))(&OctCode::decY),
169 0 <= sign[2] ? (void (OctCode ::*)(code_t))(&OctCode::incZ)
170 : (void (OctCode ::*)(code_t))(&OctCode::decZ)};
171 std::array<code_t, 3> step_v{c_origin.xStep(), c_origin.yStep(), c_origin.zStep()};
172
173 Vec3f t_max(sign[0] ? (voxel_border.x + sign[0] * grid_size / 2.0f) / dir.x : max,
174 sign[1] ? (voxel_border.y + sign[1] * grid_size / 2.0f) / dir.y : max,
175 sign[2] ? (voxel_border.z + sign[2] * grid_size / 2.0f) / dir.z : max);
176
177 Vec3f t_delta(sign[0] ? grid_size / std::abs(dir.x) : max,
178 sign[1] ? grid_size / std::abs(dir.y) : max,
179 sign[2] ? grid_size / std::abs(dir.z) : max);
180
181 // FIXME: Is this correct? Should it be zero if all zero?
182 unsigned steps = (sign[0] ? std::ceil((distance - t_max[0]) / t_delta[0]) : 0) +
183 (sign[1] ? std::ceil((distance - t_max[1]) / t_delta[1]) : 0) +
184 (sign[2] ? std::ceil((distance - t_max[2]) / t_delta[2]) : 0);
185
186 OctCode prev_at_depth = c_origin.toDepth(IntegrationGrid<OctCode>::depth() + depth);
187 IntegrationGrid<OctCode>* misses_grid = &misses[prev_at_depth];
188 misses_grid->set(c_origin);
189
190 if (ray_passthrough_hits) {
191 while (steps--) {
192 auto const advance_dim = t_max.minElementIndex();
193 (c_origin.*step_f[advance_dim])(step_v[advance_dim]);
194 t_max[advance_dim] += t_delta[advance_dim];
195
196 if (!OctCode::equalAtDepth(prev_at_depth, c_origin,
197 IntegrationGrid<OctCode>::depth() + depth)) {
198 prev_at_depth = c_origin.toDepth(IntegrationGrid<OctCode>::depth() + depth);
199 misses_grid = &misses[prev_at_depth];
200 }
201 misses_grid->set(c_origin);
202 }
203 } else {
204 auto hit_grid = hits.find(prev_at_depth);
205 auto const hit_grid_end = std::cend(hits);
206
207 while (steps-- && (hit_grid_end == hit_grid || !hit_grid->second.test(c_origin))) {
208 auto const advance_dim = t_max.minElementIndex();
209 (c_origin.*step_f[advance_dim])(step_v[advance_dim]);
210 t_max[advance_dim] += t_delta[advance_dim];
211
212 if (!OctCode::equalAtDepth(prev_at_depth, c_origin,
213 IntegrationGrid<OctCode>::depth() + depth)) {
214 prev_at_depth = c_origin.toDepth(IntegrationGrid<OctCode>::depth() + depth);
215 misses_grid = &misses[prev_at_depth];
216 hit_grid = hits.find(prev_at_depth);
217 }
218 misses_grid->set(c_origin);
219 }
220 }
221
222 // TODO: Can optimize this by calculating how many extra steps are needed above
223
224 unsigned advance_dim{};
225 std::array<unsigned, 3> s{};
226 while (s[advance_dim] != inflate_unknown) {
227 advance_dim = t_max.minElementIndex();
228 (c_origin.*step_f[advance_dim])(step_v[advance_dim]);
229 t_max[advance_dim] += t_delta[advance_dim];
230 ++s[advance_dim];
231
232 if (!OctCode::equalAtDepth(prev_at_depth, c_origin,
233 IntegrationGrid<OctCode>::depth() + depth)) {
234 prev_at_depth = c_origin.toDepth(IntegrationGrid<OctCode>::depth() + depth);
235 misses_grid = &misses[prev_at_depth];
236 }
237 misses_grid->set(c_origin);
238 }
239}
240
241template <class Map>
242void computeRaySimple(Map const& map, std::unordered_map<OctCode, Grid>& grids,
243 Vec3f origin, Vec3f goal, depth_t depth, float step_size,
244 float max_distance = std::numeric_limits<float>::max(),
245 float early_stop_distance = 0.0f)
246{
247 Vec3f dir = goal - origin;
248 float distance = dir.norm();
249 dir /= distance;
250
251 distance = std::min(distance - early_stop_distance, max_distance);
252
253 std::size_t num_steps = static_cast<std::size_t>(distance / step_size);
254 Vec3f step = dir * step_size;
255
256 OctCode prev_at_depth = map.toCode(origin, Grid::depth() + depth);
257 Grid* grid = &grids[prev_at_depth];
258 grid->set(map.toCode(origin, depth));
259 for (std::size_t i{}; i != num_steps; ++i, origin += step) {
260 OctCode cur = map.toCode(origin, depth);
261
262 if (OctCode::equalAtDepth(prev_at_depth, cur, Grid::depth() + depth)) {
263 grid = &grids[cur.toDepth(Grid::depth() + depth)];
264 prev_at_depth = cur.toDepth(Grid::depth() + depth);
265 }
266 grid->set(cur);
267 }
268}
269
270[[nodiscard]] inline std::vector<OctCode> computeRay(
271 OctKey origin, OctKey goal, float max_distance = std::numeric_limits<float>::max(),
272 std::size_t early_stop = 0, float early_stop_distance = 0.0f)
273{
274 assert(origin.depth() == goal.depth());
275
276 int const size = static_cast<int>(origin.step());
277
278 Vec3f o(static_cast<float>(origin.x), static_cast<float>(origin.y),
279 static_cast<float>(origin.z));
280 Vec3f g(static_cast<float>(goal.x), static_cast<float>(goal.y),
281 static_cast<float>(goal.z));
282
283 Vec3f dir = g - o;
284 float distance = dir.norm();
285 dir /= distance;
286
287 distance = std::min(distance, max_distance);
288
289 Vec3i step(sgn(dir.x) * size, sgn(dir.y) * size, sgn(dir.z) * size);
290
291 dir.abs();
292
293 constexpr auto max = std::numeric_limits<float>::max();
294 float f_size = static_cast<float>(size);
295
296 Vec3f t_delta(step.x ? f_size / dir.x : max, step.y ? f_size / dir.y : max,
297 step.z ? f_size / dir.z : max);
298
299 Vec3f t_max = t_delta / 2.0f;
300
301 // ray.reserve(static_cast<KeyRay::size_type>(1.5 * Vec3f::abs(g - o).norm() /
302 // f_size));
303 std::vector<OctCode> ray;
304 ray.emplace_back(origin);
305 distance -= early_stop_distance;
306 while (origin != goal && t_max.min() <= distance) {
307 auto advance_dim = t_max.minElementIndex();
308 // TODO: How to fix this case?
309 origin[advance_dim] += static_cast<key_t>(step[advance_dim]);
310 t_max[advance_dim] += t_delta[advance_dim];
311 ray.emplace_back(origin);
312 }
313 for (; 0 != early_stop && !ray.empty(); --early_stop) {
314 ray.pop_back();
315 }
316 return ray;
317}
318
319[[nodiscard]] inline std::vector<OctCode> computeRaySimple(
320 OctKey origin, OctKey goal, float step_size_factor = 1.0f,
321 float max_distance = std::numeric_limits<float>::max(), std::size_t early_stop = 0,
322 float early_stop_distance = 0.0f)
323{
324 auto const depth = origin.depth;
325
326 assert(goal.depth() == depth);
327
328 Vec3f current(static_cast<float>(origin.x), static_cast<float>(origin.y),
329 static_cast<float>(origin.z));
330 Vec3f last(static_cast<float>(goal.x), static_cast<float>(goal.y),
331 static_cast<float>(goal.z));
332
333 Vec3f dir = last - current;
334 float distance = dir.norm();
335 dir /= distance;
336
337 distance = std::min(distance, max_distance);
338
339 auto step_size = static_cast<float>(1U << depth) * step_size_factor;
340
341 std::size_t num_steps = static_cast<std::size_t>(
342 (distance - std::min(distance, early_stop_distance)) / step_size);
343 Vec3f step = dir * step_size;
344
345 num_steps = num_steps - std::min(num_steps, early_stop);
346
347 if (0 == num_steps) {
348 return {};
349 }
350
351 // FIXME: Should it take one more step?
352 std::vector<OctCode> ray;
353 ray.reserve(num_steps);
354 for (std::size_t i{}; i != num_steps; ++i, current += step) {
355 ray.emplace_back(OctKey(static_cast<key_t>(current.x), static_cast<key_t>(current.y),
356 static_cast<key_t>(current.z), depth));
357 }
358 return ray;
359}
360
361[[nodiscard]] inline std::vector<QuadCode> computeRaySimple(
362 QuadKey origin, QuadKey goal, float step_size_factor = 1.0f,
363 float max_distance = std::numeric_limits<float>::max(), std::size_t early_step = 0,
364 float early_stop_distance = 0.0f)
365{
366 // TODO: Implement
367}
368
369[[nodiscard]] inline std::vector<Vec3f> computeRaySimple(
370 Vec3f origin, Vec3f goal, float step_size,
371 float max_distance = std::numeric_limits<float>::max(), std::size_t early_stop = 0,
372 float early_stop_distance = 0.0f)
373{
374 Vec3f dir = goal - origin;
375 float distance = dir.norm();
376 dir /= distance;
377
378 distance = std::min(distance, max_distance);
379
380 distance -= early_stop_distance;
381
382 std::size_t num_steps = static_cast<std::size_t>(distance / step_size);
383 num_steps -= std::min(num_steps, early_stop);
384 Vec3f step = dir * step_size;
385
386 // FIXME: Should it take one more step?
387 std::vector<Vec3f> ray;
388 ray.reserve(num_steps);
389 for (std::size_t i{}; i != num_steps; ++i, origin += step) {
390 ray.push_back(origin);
391 }
392 return ray;
393}
394
395[[nodiscard]] inline std::vector<Vec2f> computeRaySimple(
396 Vec2f origin, Vec2f goal, float step_size,
397 float max_distance = std::numeric_limits<float>::max(), std::size_t early_stop = 0,
398 float early_stop_distance = 0.0f)
399{
400 Vec2f dir = goal - origin;
401 float distance = dir.norm();
402 dir /= distance;
403
404 distance = std::min(distance, max_distance);
405
406 distance -= early_stop_distance;
407
408 std::size_t num_steps = static_cast<std::size_t>(distance / step_size);
409 num_steps -= std::min(num_steps, early_stop);
410 Vec2f step = dir * step_size;
411
412 // FIXME: Should it take one more step?
413 std::vector<Vec2f> ray;
414 ray.reserve(num_steps);
415 for (std::size_t i{}; i != num_steps; ++i, origin += step) {
416 ray.push_back(origin);
417 }
418 return ray;
419}
420} // namespace ufo
421
422#endif // UFO_MAP_RAY_CASTER_HPP
constexpr int sign(T val) noexcept
Returns the sign of a value.
Definition math.hpp:70
STL namespace.
All vision-related classes and functions.
Definition cloud.hpp:49
constexpr Vec< Dim, T > ceil(Vec< Dim, T > v) noexcept
Returns the component-wise ceiling of a floating-point vector.
Definition vec.hpp:1390
constexpr Vec< Geometry::dimension(), typename Geometry::value_type > max(Geometry const &g)
Returns the maximum coordinate of the minimum spanning axis-aligned bounding box of a geometry.
Definition fun.hpp:72
constexpr Vec< Dim, T > abs(Vec< Dim, T > v) noexcept
Returns the component-wise absolute value of a vector.
Definition vec.hpp:1351
constexpr auto distance(A const &a, B const &b)
Computes the minimum distance between two shapes.
Definition distance.hpp:61