VIPRA Documentation
Loading...
Searching...
No Matches
astar_algo.hpp
1#pragma once
2
3#include <algorithm>
4#include <limits>
5#include <optional>
6#include <vector>
7
8#include "goals/astar/douglas_peucker.hpp"
9#include "goals/astar/pathing_graph.hpp"
10#include "vipra/geometry/f3d.hpp"
11#include "vipra/types/float.hpp"
12#include "vipra/types/idx.hpp"
13
14#include "vipra/macros/performance.hpp"
15
16namespace VIPRA {
17
18struct Node {
19 VIPRA::idx self;
20 VIPRA::idx parent;
21 VIPRA::f_pnt distanceFromStart;
22 VIPRA::f_pnt distanceWithHeuristic;
23
24 VIPRA_INLINE auto operator==(Node const& other) const noexcept -> bool
25 {
26 return self == other.self;
27 }
28
29 VIPRA_INLINE auto operator<(Node const& other) const noexcept -> bool
30 {
31 return distanceWithHeuristic > other.distanceWithHeuristic;
32 }
33};
34
35[[nodiscard]] inline auto astar(
36 VIPRA::f3d endPos, VIPRA::idx start, VIPRA::idx end, Goals::PathingGraph const& graph,
37 VIPRA::f_pnt smoothingEpsilon) noexcept -> std::optional<std::vector<VIPRA::f3d>>
38{
39 auto const startPos = graph.pos(start);
40 auto const endGridPos = graph.pos(end);
41
42 std::vector<Node> nodes(graph.node_count());
43 std::vector<Node> openQueue;
44 std::vector<bool> openSet(graph.node_count());
45 std::vector<bool> closedSet(graph.node_count());
46
47 nodes[start] = Node{start, start, 0, startPos.distance_to(endGridPos)};
48
49 Node curr{};
50 openSet[start] = true;
51 openQueue.push_back(nodes[start]);
52
53 while ( ! openQueue.empty() ) {
54 curr = openQueue.front();
55
56 if ( curr.self == end ) break;
57
58 std::pop_heap(openQueue.begin(), openQueue.end());
59 openQueue.pop_back();
60
61 openSet[curr.self] = false;
62 closedSet[curr.self] = true;
63
64 for ( VIPRA::idx const neighborIdx : graph.neighbors(curr.self) ) {
65 if ( neighborIdx == std::numeric_limits<VIPRA::idx>::max() ) continue;
66 if ( closedSet[neighborIdx] ) continue;
67
68 auto const& currPos = graph.pos(curr.self);
69 auto const& neighborPos = graph.pos(neighborIdx);
70 Node const neighbor{
71 neighborIdx, curr.self,
72 nodes[curr.self].distanceFromStart + currPos.distance_to(neighborPos),
73 nodes[curr.self].distanceFromStart + currPos.distance_to(neighborPos) +
74 neighborPos.distance_to(endGridPos)};
75
76 if ( ! openSet[neighborIdx] ) {
77 nodes[neighborIdx] = neighbor;
78 openSet[neighborIdx] = true;
79 openQueue.push_back(neighbor);
80 std::push_heap(openQueue.begin(), openQueue.end());
81 continue;
82 }
83
84 if ( neighbor.distanceFromStart < nodes[neighborIdx].distanceFromStart ) {
85 nodes[neighborIdx] = neighbor;
86 }
87 }
88 }
89
90 if ( curr.self != end ) {
91 // no path found
92 return std::nullopt;
93 }
94
95 std::vector<VIPRA::f3d> path;
96 VIPRA::f3d dif;
97
98 path.push_back(endPos); // skip last grid and go straight to exact end position
99 curr = nodes[curr.parent];
100
101 while ( true ) {
102 // loop back through nodes creating the path, squashing every node that goes in the same direction into one
103 curr = nodes[curr.parent];
104
105 if ( curr.parent == start ) break; // skip first grid, since ped is already there
106
107 auto currDif = graph.pos(curr.self) - graph.pos(curr.parent);
108 if ( currDif != dif ) {
109 path.push_back(graph.pos(curr.parent));
110 dif = currDif;
111 }
112 }
113
114 return VIPRA::douglas_peucker_algo(path, smoothingEpsilon);
115}
116} // namespace VIPRA
Definition pathing_graph.hpp:19
Definition astar_algo.hpp:18
Definition f3d.hpp:27