DHART
Loading...
Searching...
No Matches
graph_utils.cpp
Go to the documentation of this file.
1#include <graph_generator.h>
2
3#include <HitStruct.h>
4#include <Constants.h>
5#include <Edge.h>
6#include <embree_raytracer.h>
7#include <ray_data.h>
8#include <cassert>
9
10namespace HF::GraphGenerator {
11
13 static const real3 down = { 0,0,-1 };
14
17
21
22 using std::vector;
23
35 template<typename point_type>
36 inline Node ToNode(const point_type& ct) {
37 return Node(ct[0], ct[1], ct[2]);
38 }
39
53 template<typename n1_type, typename n2_type>
54 inline real_t DistanceTo(const n1_type& n1, const n2_type& n2) {
55 return sqrt(pow((n1[0] - n2[0]), 2) + pow((n1[1] - n2[1]), 2) + pow((n1[2] - n2[2]), 2));
56 }
57
67 template<typename vector_type>
68 inline void Normalize(vector_type& v) {
69 auto magnitude = sqrt(pow(v[0], 2) + pow(v[1], 2) + pow(v[2], 2));
70 v[0] = v[0] / magnitude;
71 v[1] = v[1] / magnitude;
72 v[2] = v[2] / magnitude;
73 }
74
87 template<typename vector_type>
88 inline vector_type DirectionTo(const vector_type& n1, const vector_type& n2) {
89 vector_type direction_vector;
90
91 direction_vector[0] = n2[0] - n1[0];
92 direction_vector[1] = n2[1] - n1[1];
93 direction_vector[2] = n2[2] - n1[2];
94
95 // Normalize the vector
96 Normalize(direction_vector);
97 return direction_vector;
98 }
99
101 static const vector<pair> init_directs = {
102 pair(-1, -1), pair(-1, 0), pair(-1, 1),
103 pair(0, -1), pair(0, 1), pair(1, -1),
104 pair(1, 0), pair(1, 1)
105 };
106
107 optional_real3 ValidateStartPoint(RayTracer& RT, const real3& start_point, const GraphParams& Params)
108 {
109 return CheckRay(RT, start_point, down, Params.precision.node_z, HIT_FLAG::FLOORS, Params.geom_ids);
110 }
111
126 inline bool CheckGeometryID(HIT_FLAG goal, int id, const GeometryFlagMap & geom_dict) {
127
128 // If the target is both or the geometry rules are set to NO_FLAG, all hits are counted as
129 // being on walkable geometry
131 return true;
132
133 // Otherwise do different checks based on the ruleset
134 else if (geom_dict.Mode == GeometryFilterMode::OBSTACLES_ONLY)
135 // If only obstacles are specified, this works like a blacklist/whitelist
136 if (goal == OBSTACLES)
137 return geom_dict[id] == HIT_FLAG::OBSTACLES;
138 else
139 return geom_dict[id] != HIT_FLAG::OBSTACLES;
140
142 // In OBSTACLES_AND_FLOORS mode, the id's type must exactly match the goal
143 return (goal == geom_dict[id]);
144 }
145
147 RayTracer& ray_tracer,
148 const real3& origin,
149 const real3& direction,
150 real_t node_z_tolerance,
151 HIT_FLAG flag,
152 const GeometryFlagMap & geometry_dict)
153 {
154 // Setup default params
156
157 // Cast the ray. On success, this returns the ID and distance to intersection.
158 res = ray_tracer.Intersect(origin, direction);
159
160 // Check if it hit and the ID of the geometry matches what we were looking for.
161 if (res.DidHit() && CheckGeometryID(flag, res.meshid, geometry_dict)) {
162 // Create a new optional point with a copy of the origin
163 optional_real3 return_pt(origin);
164
165 // Move it in direction
166 MoveNode(res.distance, direction, *return_pt);
167 //double temp_diff = (origin[2]) - dist; // Sanity check for direction -1 to see influence of movenode arithmetic
168 // Round the position to the z value tolerance
169 return_pt.pt[2] = HF::SpatialStructures::roundhf_tail<real_t>(return_pt.pt[2], 1/node_z_tolerance);
170
171 // Return the optional point
172 return return_pt;
173 }
174
175 // Otherwise, return an empty optional_real3 to signal that
176 // the point didn't intersect
177 return optional_real3();
178 }
179
180 std::set<std::pair<int, int>> permutations(int limit) {
181 // Create a vector of all numbers between 1 and limit + 1, as well as their inverses
182 vector<int> steps;
183 steps.reserve(2 * limit);
184
185 for (int i = 1; i < limit + 1; i++) {
186 steps.push_back(i);
187 steps.push_back(-i);
188 }
189
190 std::set<std::pair<int, int>> perms;
191
192 // Nest a for loop to capture every permutation
193 for (int j : steps)
194 for (int k : steps)
195 if (abs(j) != abs(k))
196 perms.emplace(pair(j, k));
197
198 return perms;
199 }
200
201 vector<pair> CreateDirecs(int max_step_connections) {
202 // A max_step_connections of 1 is just init_directs
203 if (max_step_connections == 1) return init_directs;
204
205 // Otherwise generate extra directions
206 auto perms = permutations(max_step_connections);
207
208 // Copy init_directs into our output array
209 vector<pair> out_directions = init_directs;
210
211 // Resize our output array to fit the new permutations, then fill it
212 out_directions.resize(out_directions.size() + perms.size());
213 std::move(perms.begin(), perms.end(), out_directions.begin() + init_directs.size());
214
215 return out_directions;
216 }
217
218 vector<graph_edge> GetChildren(
219 const real3& parent,
220 const vector<real3>& possible_children,
221 RayTracer& rt,
222 const GraphParams& GP
223 )
224 {
225 std::vector<graph_edge> valid_edges;
226
227 // Call CheckChildren to get rid of all children that aren't over valid ground or don't meet our upstep
228 // and downstep requirements. This array of children will also be moved directly ontop of the ground their over.
229 const auto checked_children = CheckChildren(parent, possible_children, rt, GP);
230
231 // Iterate through every child in the checked children
232 for (const auto& child : checked_children)
233 {
234 // Determine the type of connection between the parent and child
235 // including if it is a step, slope, or not connected
236 const STEP connection_type = CheckConnection(parent, child, rt, GP);
237
238 // If the node is connected Add it to out list of valid children
239 if (connection_type != STEP::NOT_CONNECTED)
240 {
241 // Add the edge to the array of children, storing the distance and connection type
242 valid_edges.emplace_back(graph_edge(ToNode(child), DistanceTo(parent, child), connection_type));
243 }
244 }
245 return valid_edges;
246 }
247
248 std::vector<real3> CheckChildren(
249 const real3& parent,
250 const std::vector<real3>& possible_children,
251 RayTracer& rt,
252 const GraphParams& GP)
253 {
254 vector<real3> valid_children;
255
256 // Iterate through every child in the set of possible children
257 for (const auto& child : possible_children)
258 {
259
260 // Check if a ray intersects a mesh
261 optional_real3 potential_child = CheckRay(rt, child, down, GP.precision.node_z, HIT_FLAG::FLOORS, GP.geom_ids);
262
263 if (potential_child)
264 {
265 // Get a reference to the child in the return value now that we know it's over valid ground
266 auto& confirmed_child = potential_child.pt;
267
268 // TODO: this is a premature check and should be moved to the original calling function
269 // after the step type check since upstep and downstep are parameters for stepping and not slope
270
271 // Check to see if the new position will satisfy up and downstep restrictions
272 real_t dstep = parent[2] - confirmed_child[2];
273 real_t ustep = confirmed_child[2] - parent[2];
274
275
276 if (dstep < GP.down_step && ustep < GP.up_step)
277 valid_children.push_back(confirmed_child);
278 }
279 }
280 return valid_children;
281 }
282
283 bool OcclusionCheck(const real3& parent, const real3& child, RayTracer& RT)
284 {
285 // Use the distance between parent and child
286 // as the maximum distance for the occlusion check
287 return RT.Occluded(parent, DirectionTo(parent, child), DistanceTo(parent, child));
288 }
289
290 bool CheckSlope(const real3& parent, const real3& child, const GraphParams& gp)
291 {
292 // Slope is rise/run
293 const real_t run = sqrt(pow((parent[0] - child[0]), 2) + pow((parent[1] - child[1]), 2));
294 const real_t rise = child[2] - parent[2];
295
296 // Calculate the angle between rise and run.
297 const real_t calc_slope = atan2(rise, run) * (static_cast<real_t>(180.00) / static_cast<real_t>(M_PI));
298
299 // Check against downslope and upslope.
300 return calc_slope > -1.0 * gp.down_slope && calc_slope < gp.up_slope;
301 }
302
304 const real3& parent,
305 const real3& child,
306 RayTracer& rt,
307 const GraphParams& params)
308 {
309 // Get groundoffset from graph parameters
310 const auto GROUND_OFFSET = params.precision.ground_offset;
311
312 // Create a copy of parent and child that can be modified
313 auto node1 = parent;
314 auto node2 = child;
315
316 // Offset them from the ground slightly
317 node1[2] += GROUND_OFFSET;
318 node2[2] += GROUND_OFFSET;
319
320 // See if there's a direct line of sight between parent and child
321 if (!OcclusionCheck(node1, node2, rt)) {
322 // If there is a direct line of sight, and they're on the same plane
323 // then there is no step.
324 if (abs(node1[2] - node2[2]) < GROUND_OFFSET) return STEP::NONE;
325
326 // If they are not on the same plane, it means this is a slope. Check if the slope is within the threshold.
327 else if (CheckSlope(parent, child, params)) return STEP::NONE;
328
329 return STEP::NOT_CONNECTED;
330 }
331
332 // Otherwise check for a step based connectiom
333 else {
334 STEP s = STEP::NONE;
335 // If parent is higher than child, the check is to go downstairs
336 // Since the child is lower, raise the child height by the downstep limit
337 // to be checked for a connection
338 if (parent[2] > child[2])
339 {
340 node1 = child;
341 node2 = parent;
342 node1[2] = node1[2] + params.down_step;
343 node2[2] = node2[2] + GROUND_OFFSET;
344 s = STEP::DOWN;
345 }
346
347 // If parent is lower than child, the check is to go upstairs
348 // Since the child is lower, raise the child height by the upstep limit
349 // to be checked for a connection
350 else if (node1[2] < node2[2])
351 {
352 node1 = parent;
353 node2 = child;
354 node1[2] = node1[2] + params.up_step;
355 node2[2] = node2[2] + GROUND_OFFSET;
356 s = STEP::UP;
357 }
358
359 // If they're on an equal plane then offset by upstep to see
360 // if the obstacle can be stepped over.
361 else if (node1[2] == node2[2])
362 {
363 node1 = parent;
364 node2 = child;
365 node1[2] = node1[2] + params.up_step;
366 node2[2] = node2[2] + GROUND_OFFSET;
367 s = STEP::OVER;
368 }
369
370 // If there is a line of sight then the nodes are connected
371 // with the step type we calculated
372 if (!OcclusionCheck(node1, node2, rt))
373 return s;
374 }
375
376 // If not, then there is no connection between these
377 // nodes.
378 return STEP::NOT_CONNECTED;
379 }
380
381 std::vector<real3> GeneratePotentialChildren(
382 const real3& parent,
383 const std::vector<pair>& directions,
384 const real3& spacing,
385 const GraphParams& GP)
386 {
387 // Create a vector of out_children to hold results
388 vector<real3> out_children(directions.size());
389
390 // Iterate through the set of all directions, then offset children in each direction
391 for (int i = 0; i < directions.size(); i++) {
392 // Get the direction at this index
393 const auto& dir = directions[i];
394
395 // Extract the x and y directions
396 real_t x_offset = dir.first;
397 real_t y_offset = dir.second;
398
399 // Add the user-defined spacing to the x and y components of the parent.
400 // Then round the result.
401 const real_t x = roundhf_tmp<real_t>(std::fma(x_offset,spacing[0], parent[0]), GP.precision.node_spacing);
402 const real_t y = roundhf_tmp<real_t>(std::fma(y_offset,spacing[1], parent[1]), GP.precision.node_spacing);
403 // Round the z value to a lower precision assuming it helps embree
404 const real_t z = roundhf_tmp<real_t>(parent[2] + spacing[2], GP.precision.node_z);
405
406 // Add these new values as a node in the out_children list
407 out_children[i] = real3{ x, y, z };
408 }
409
410 return out_children;
411 }
412}
Contains declarations for all functions related to the graph generator.
Contains definitions for the EmbreeRayTracer
Contains definitions for the HF::SpatialStructures namespace.
Contains definitions for the Edge structure.
Generate a graph of accessible space from a given start point.
Holds parameters for the GraphGenerator.
real_t up_step
Maximum height of a step the graph can traverse.Any steps higher this will be considered inaccessible...
HF::SpatialStructures::Edge graph_edge
Type of edge for the graph generator to use internally.
std::vector< real3 > GeneratePotentialChildren(const real3 &parent, const std::vector< pair > &direcs, const real3 &spacing, const GraphParams &gp)
Populare out_children with a potential child position for every direction in directions.
std::vector< real3 > CheckChildren(const real3 &parent, const std::vector< real3 > &possible_children, RayTracer &rt, const GraphParams &params)
Determine whether children are over valid ground, and and meet upstep/downstep requirements.
real_t ground_offset
Distance to offset nodes from the ground before checking line of sight.
void MoveNode(const A &dist, const D &direction, N &node)
Move a node in direction by dist units.
GeometryFlagMap geom_ids
Stores a map of geometry IDs to their HIT_FLAGS and the current filter mode of the graph.
HIT_FLAG
Determines which geometry the ray will collide with.
@ OBSTACLES
Obstacles only.
@ BOTH
Collide with floors and obstacles.
@ FLOORS
Floors only.
double real_t
Internal decimal type of the graph generator.
real_t node_z
Precision to round the z-component of nodes after a raycast is performed.
optional_real3 CheckRay(RayTracer &RT, const real3 &origin, const real3 &direction, real_t node_z_tolerance, HIT_FLAG flag=BOTH, const GeometryFlagMap &geometry_dict=GeometryFlagMap())
Cast a ray and get the point of intersection if it connects.
vector_type DirectionTo(const vector_type &n1, const vector_type &n2)
Calculate the normalized direction from one node to another.
Definition: graph_utils.cpp:88
real_t down_slope
The maximum downward slope the graph can traverse. Any slopes steeper than this will be considered in...
real_t DistanceTo(const n1_type &n1, const n2_type &n2)
Calculate the distance between two nodes.
Definition: graph_utils.cpp:54
std::array< real_t, 3 > real3
Type used for the standard coordinate set of the graph generator.
static const real3 down
Constant used as a direction for downwards raycasts.
Definition: graph_utils.cpp:13
bool CheckSlope(const real3 &parent, const real3 &child, const GraphParams &gp)
Determine if the slope between parent and child is traversable according to the graph parameters.
optional_real3 ValidateStartPoint(RayTracer &RT, const real3 &start_point, const GraphParams &Params)
Determine if the start point of the graph is over valid ground.
std::pair< int, int > pair
Type for Directions to be stored as.
bool CheckGeometryID(HIT_FLAG goal, int id, const GeometryFlagMap &geom_dict)
Determine if a hit is against the geometry type specified.
std::vector< graph_edge > GetChildren(const real3 &parent, const std::vector< real3 > &possible_children, RayTracer &rt, const GraphParams &GP)
Calculate all possible edges between parent and possible_children.
Node ToNode(const point_type &ct)
Convert a point_type to a node.
Definition: graph_utils.cpp:36
real_t node_spacing
Precision to round nodes after spacing is calculated.
bool OcclusionCheck(const real3 &parent, const real3 &child, RayTracer &RT)
Determine if there is a valid line of sight between parent and child.
void Normalize(vector_type &v)
Normalize a vector.
Definition: graph_utils.cpp:68
static const vector< pair > init_directs
real_t down_step
Maximum step down the graph can traverse.Any steps steeper than this will be considered inaccessible.
real_t up_slope
Maximum upward slope the graph can traverse in degrees.Any slopes steeper than this will be considere...
HF::SpatialStructures::STEP CheckConnection(const real3 &parent, const real3 &child, RayTracer &rt, const GraphParams &params)
Determine what kind of step (if any) is between parent and child.
std::vector< pair > CreateDirecs(int max_step_connections)
Create a set of directions based on max_step_connections.
Precision precision
Tolerances for the graph.
@ ALL_INTERSECTIONS
Consider all geometry as walkable surfaces.
@ OBSTACLES_ONLY
Consider all geometry that isn't an obstacle as walkable.
@ OBSTACLES_AND_FLOORS
Explicitly tag geometry ids as either obstacle or floor. Any ids outside of these ranges will always ...
std::set< std::pair< int, int > > permutations(int limit)
Calculate P(n,r) as an array with each unique permutaton of 2 values being a pair.
constexpr numeric_type roundhf_tmp(numeric_type f, numeric_type p, numeric_type r)
round a number to the nearest precision defined globally. The global values can be overridden with op...
Definition: Constants.h:114
constexpr numeric_type trunchf_tmp(numeric_type f, numeric_type p, numeric_type r)
truncate a number to the nearest precision defined globally. The global values can be overridden with...
Definition: Constants.h:170
STEP
Describes the type of step an edge connects to.
Definition: Edge.h:15
Manages rules and ids for different types of geometry in the graph generator.
GeometryFilterMode Mode
Filter mode of this GeometryFlagMap.
A simple wrapper for real3 that is able to determine whether or not it's defined.
A simple hit struct to carry all relevant information about hits.
Definition: HitStruct.h:7
bool DidHit() const
Determine whether or not this hitstruct contains a hit.
Definition: HitStruct.h:17
numeric_type distance
Distance from the origin point to the hit point. Set to -1 if no hit was recorded.
Definition: HitStruct.h:8
int meshid
The ID of the hit mesh. Set to -1 if no hit was recorded.
Definition: HitStruct.h:9
bool Occluded(const real3 &origin, const real3 &direction, real_t distance)
Definition: MultiRT.cpp:49
HitStruct< real_t > Intersect(const real3 &origin, const real3 &direction)
Definition: MultiRT.cpp:40
A connection to a child node.
Definition: Edge.h:29
A point in space with an ID.
Definition: node.h:38