DHART
Loading...
Searching...
No Matches
cost_algorithms.cpp
Go to the documentation of this file.
1
8
9#define _USE_MATH_DEFINES
10#include <cmath>
11#include <algorithm>
12
13#include "cost_algorithms.h"
14#include "graph.h"
15#include "Constants.h"
16
17#include <iostream>
18
24
26 double to_radians(double degrees) {
27 return degrees * (M_PI / 180);
28 }
29
30 double to_degrees(double radians) {
31 return radians * (180 / M_PI);
32 }
33
34 double CalculateSlope(Node& parent, Node& child)
35 {
36 // Calculates the Slope between two nodes as an angle in degrees.
37 // This could be split into two functions later since the first commented part is simply rise/run
38 // The second part is a generic cosine angle between vectors
39
40 // Calculate slope with Rise over Run using atan2
41 /*
42 double run = std::sqrt( std::pow(parent[0]-child[0],2) + std::pow(parent[1]-child[1],2) );
43 double rise = parent[2] - child[2];
44 double slope = to_degrees(std::atan2(rise, run));
45 */
46
47 // Calculate slope with cosine of two vectors
48
49 // Get the vector between the points for the first vector
50 double n1 = child[0] - parent[0];
51 double n2 = child[1] - parent[1];
52 double n3 = child[2] - parent[2];
53
54 // Second vector is the z unit vector
55 double u1 = 0;
56 double u2 = 0;
57 double u3 = 1;
58
59 // double numerator = std::abs((n1 * u1) + (n2 * u2) + (n3 * u3));
60 // Which reduces to:
61 double numerator = std::abs(n3);
62
63 // Distance of first vector
64 double denom = std::sqrt(std::pow(n1, 2) + std::pow(n2, 2) + std::pow(n3, 2));
65
66 // Distance of second vector
67 // double denom_2 = std::abs(std::sqrt(std::pow(u1, 2) + std::pow(u2, 2) + std::pow(u3, 2)));
68 // Which reduces to:
69 // double denom_2 = std::sqrt(std::pow(u3, 2));
70 // and then
71 // double denom_2 = u3;
72 // Which is 1
73
74 double res = std::asin(numerator/denom);
75
76 int direc;
77 if (child[2] > parent[2]){
78 direc = 1;
79 }
80
81 else{
82 direc = -1;
83 }
84
85 double slope = to_degrees(res) * direc;
86
87 // This will catch nan, infinity, and negative infinity.
88 // All of these values would break the calculation.
89 if (!std::isfinite(slope))
90 slope = 0;
91
92 return slope;
93
94 }
95
97 // Energy expenditure data will be stored here and returned from this function.
98 std::vector<EdgeSet> edge_set;
99
100 // From Subgraph sg
101 Node parent_node = sg.m_parent;
102 std::vector<Edge> edge_list = sg.m_edges;
103
104 // We can preallocate this container to have edge_list.size()
105 // blocks since we know exactly how many children make up the subgraph.
106 std::vector<IntEdge> children(edge_list.size());
107 auto it_children = children.begin();
108
109 for (Edge link_a : edge_list) {
110 // Retrieve vector components of the vector formed by
111 // parent_node and link_a
112
113 const auto dir = parent_node.directionTo(link_a.child);
114 const auto magnitude = parent_node.distanceTo(link_a.child);
115
116 //
117 // Angle formula in R3 is:
118 // acos(dot_product(vector_U, vector_V) / magnitude(vector_U) * magnitude(vector_V));
119 //
120 // vector_V will be a unit vector i, j, or k for x, y, and z axes respectively.
121 // Therefore, we simplify these angle formulas to:
122 // acos(component_value / magnitude(vector_U))
123 // component_value will be dir[0], dir[1], or dir[2] for x, y, or z components respectively.
124 //
125 //auto angle_x_axis = to_degrees(std::acos(dir[0] / magnitude));
126 //auto angle_y_axis = to_degrees(std::acos(dir[1] / magnitude));
127 //auto angle_z_axis = to_degrees(std::acos(dir[2] / magnitude));
128
129 //auto angle = angle_y_axis;
130 //auto slope = std::clamp(std::tanf(angle), -0.4f, -0.4f);
131
132 const double slope = CalculateSlope(parent_node, link_a.child);
133
134 const double g = std::clamp(std::tan(to_radians(slope)), -0.4, 0.4);
135
136 auto e = 280.5
137 * (std::pow(g, 5)) - 58.7
138 * (std::pow(g, 4)) - 76.8
139 * (std::pow(g, 3)) + 51.9
140 * (std::pow(g, 2)) + 19.6
141 * (g) + 2.5;
142
143 // You cannot gain energy. This indicates a programmer error.
144 assert(e >= 0);
145
146 // Calculate the new score/distance for the IntEdge
147 double expenditure = e * magnitude;
148
149 // Create the resulting IntEdge from the current child ID and calculation
150 IntEdge ie = { link_a.child.id, static_cast<float>(expenditure) };
151
152 // Dereference the vector<IntEdge> iterator, assign it to the IntEdge created,
153 // then advance the iterator.
154 *(it_children++) = ie;
155 }
156
157 EdgeSet es = { parent_node.id, children };
158
159 return es;
160 }
161
162 std::vector<EdgeSet> CalculateEnergyExpenditure(const Graph& g) {
163 // Retrieve all nodes from g so we can obtain subgraphs.
164 std::vector<Node> nodes = g.Nodes();
165
166 // The result container will always be, at most, the node count of g.
167 // We can preallocate this memory so we do not have to resize during the loop below.
168 std::vector<EdgeSet> result(nodes.size());
169 auto it_result = result.begin();
170
171 for (Node parent_node : nodes) {
172 // For all Node in g...
173
174 // Get the Subgraph via parent_node
175 Subgraph sg = g.GetSubgraph(parent_node);
176
177 // Call CalculateEnergyExpenditure using the returned Subgraph,
178 // get a vector<EdgeSet> back
179 EdgeSet energy_expenditures = CalculateEnergyExpenditure(sg);
180
181 // Dereference it_result, assign it to the energy_expenditures container,
182 // then advance it_result (std::vector<std::vector<EdgeSet>>::iterator)
183 *(it_result++) = energy_expenditures;
184 }
185
186 return result;
187 }
188
189 std::vector<IntEdge> CalculateCrossSlope(const Subgraph& sg) {
190 // All cross slope data for Subgraph sg will be stored here
191 // and returned from this function.
192 std::vector<IntEdge> result;
193
194 Node parent_node = sg.m_parent;
195 std::vector<Edge> edges = sg.m_edges;
196
197 for (Edge edge_a : edges) {
198 // We iterate over all edges that extend from parent_node.
199 Node child_node_a = edge_a.child;
200 float edge_data_a = edge_a.score;
201
202 //
203 // Here -- we should assess the step_type field for edge_a.
204 //
205 // /// <summary> Describes the type of step an edge connects to. </summary>
206 // enum STEP {
207 // NOT_CONNECTED = 0, ///< No connection between parent and child.
208 // NONE = 1, ///< Parent and child are on the same plane and no step is required.
209 // UP = 2, ///< A step up is required to get from parent to child.
210 // DOWN = 3, ///< A step down is required to get from parent to child.
211 // OVER = 4 ///< A step over something is required to get from parent to child.
212 // };
213 //
214
215 // We must have a container to store all perpendicular edges found.
216 // This container will have all edges that are perpendicular to edge_a --
217 // or rather, the vector formed by parent_node and child_node_a.
218
219 // Note that we are checking for perpendicularity in 2D space only.
220 std::vector<Edge> perpendicular_edges = GetPerpendicularEdges<2>(sg, child_node_a);
221
222 float weight = 0.0;
223 float cross_slope = 0.0;
224 float a_z = 0.0;
225 float b_z = 0.0;
226 float c_z = 0.0;
227
228 switch (perpendicular_edges.size()) {
229 case 0:
230 // No edges were found to be perpendicular to the edge
231 // formed by node parent_node and node child_node_a.
232 // The IntEdge to be created will use the existing edge data
233 // of parent_node and child_node_a (edge_data_a).
234 weight = edge_data_a;
235 break;
236 case 1:
237 // One edge formed by node parent_node and another child_node
238 // was found to be perpendicular to the edge formed by
239 // node parent_node and node child_node_a.
240
241 a_z = child_node_a.z;
242
243 // z value of other child_node
244 b_z = perpendicular_edges[0].child.z;
245
246 cross_slope = std::abs(a_z - b_z);
247
248 // add the existing weight value
249 weight = cross_slope + perpendicular_edges[0].score;
250 break;
251 case 2:
252 // Two edges -- each formed by node parent_node and two separate child node
253 // were found to be perpendicular to the edge formed by
254 // node parent_node and node child_node_a.
255
256 a_z = child_node_a.z;
257
258 // z value of the first other child_node
259 b_z = perpendicular_edges[0].child.z;
260
261 // z value of the second other child node
262 c_z = perpendicular_edges[1].child.z;
263
264 cross_slope = std::abs(b_z - c_z);
265
266 // add the existing weight value
267 weight = std::abs(b_z - c_z) + perpendicular_edges[0].score;
268 break;
269 default:
270 break;
271 }
272
273 // Create the IntEdge using child_node_a.id
274 // and the (cross slope value + existing edge score)
275 // -- then add it to our result container.
276 IntEdge ie = { child_node_a.id, weight };
277 result.push_back(ie);
278 }
279
280 return result;
281 }
282
283 std::vector<std::vector<IntEdge>> CalculateCrossSlope(const Graph& g) {
284 // Retrieve all nodes from g so we can obtain subgraphs.
285 std::vector<Node> nodes = g.Nodes();
286
287 // The result container will always be, at most, the node count of g.
288 // We can preallocate this memory so we do not have to resize during the loop below.
289 std::vector<std::vector<IntEdge>> result(nodes.size());
290 auto it_result = result.begin();
291
292 for (Node parent_node : nodes) {
293 // For all Node in g...
294
295 // Get the Subgraph via parent_node
296 Subgraph sg = g.GetSubgraph(parent_node);
297
298 // Call CalculateCrossSlope using the returned Subgraph,
299 // get a vector<IntEdge> back
300 std::vector<IntEdge> cross_slopes = CalculateCrossSlope(sg);
301
302 // Dereference it_result, assign it to the cross_slopes container,
303 // then advance it_result (std::vector<std::vector<IntEdge>>::iterator)
304 *(it_result++) = cross_slopes;
305 }
306
307 return result;
308 }
309}
Contains definitions for the HF::SpatialStructures namespace.
Contains implementation for the HF::SpatialStructures::CostAlgorithms namespace.
Contains definitions for the Graph class.
A Subgraph consists of a parent Node m_parent and a container of Edge m_edges such that all Edge in m...
Definition: graph.h:364
Node m_parent
The parent node from which all Edge in m_edges extend.
Definition: graph.h:365
std::vector< Edge > m_edges
The edges that extend from m_parent.
Definition: graph.h:366
std::vector< IntEdge > CalculateCrossSlope(const Subgraph &sg)
EdgeSet CalculateEnergyExpenditure(const Subgraph &sg)
double CalculateSlope(Node &parent, Node &child)
A connection to a child node.
Definition: Edge.h:29
A lighter version of Edge that contains an ID instead of a full node object..
Definition: Edge.h:58
int child
Identifier of child node.
Definition: Edge.h:59
A collection of edges and a parent.
Definition: Edge.h:77
A Graph of nodes connected by edges that supports both integers and HF::SpatialStructures::Node.
Definition: graph.h:486
Subgraph GetSubgraph(const Node &parent_node, const std::string &cost_type="") const
Retrieves a Subgraph using a Node.
std::vector< Node > Nodes() const
Get a list of nodes from the graph sorted by ID.
A point in space with an ID.
Definition: node.h:38
float z
Cartesian coordinates x, y, z.
Definition: node.h:40
int id
Node identifier.
Definition: node.h:42
float distanceTo(const Node &n2) const
Calculate the distance between this node and n2.
Definition: node.cpp:40
std::array< float, 3 > directionTo(const Node &n2) const
Get the direction between this node and another node
Definition: node.cpp:131