载入中...
搜索中...
未找到
Steering.cpp
浏览该文件的文档.
1#include "math/Steering.h"
2
3#include <algorithm>
4#include <cmath>
5#include <cstddef>
6
8namespace {
9
10bool finite(Vector2 value) { return std::isfinite(value.x) && std::isfinite(value.y); }
11bool finite(Vector3 value) {
12 return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z);
13}
14
15template <typename Vector>
16double distance(Vector lhs, Vector rhs) {
17 const double dx = static_cast<double>(lhs.x) - static_cast<double>(rhs.x);
18 const double dy = static_cast<double>(lhs.y) - static_cast<double>(rhs.y);
19 if constexpr (requires { lhs.z; }) {
20 const double dz = static_cast<double>(lhs.z) - static_cast<double>(rhs.z);
21 return std::hypot(dx, dy, dz);
22 }
23 return std::hypot(dx, dy);
24}
25
26template <typename Vector>
27Vector scaledDirection(Vector from, Vector to, float magnitude) {
28 if (!finite(from) || !finite(to) || !(magnitude > 0.f) || !std::isfinite(magnitude))
29 return {};
30 const double dx = static_cast<double>(to.x) - static_cast<double>(from.x);
31 const double dy = static_cast<double>(to.y) - static_cast<double>(from.y);
32 const double length = distance(from, to);
33 if (!(length > 0.0) || !std::isfinite(length)) return {};
34 if constexpr (requires { from.z; }) {
35 const double dz = static_cast<double>(to.z) - static_cast<double>(from.z);
36 return {static_cast<float>(dx / length * magnitude),
37 static_cast<float>(dy / length * magnitude),
38 static_cast<float>(dz / length * magnitude)};
39 }
40 return {static_cast<float>(dx / length * magnitude),
41 static_cast<float>(dy / length * magnitude)};
42}
43
44template <typename Vector>
45Vector seekImpl(Vector position, Vector target, float maxSpeed) {
46 return scaledDirection(position, target, maxSpeed);
47}
48
49template <typename Vector>
50Vector arriveImpl(Vector position, Vector target, float maxSpeed, float slowRadius,
51 float stopRadius) {
52 if (!finite(position) || !finite(target) || !std::isfinite(slowRadius) ||
53 !std::isfinite(stopRadius) || slowRadius <= stopRadius || stopRadius < 0.f)
54 return {};
55 const double targetDistance = distance(position, target);
56 if (targetDistance <= stopRadius) return {};
57 const double speed = static_cast<double>(maxSpeed) *
58 std::min(1.0, (targetDistance - stopRadius) /
59 (static_cast<double>(slowRadius) - stopRadius));
60 return scaledDirection(position, target, static_cast<float>(speed));
61}
62
63template <typename Vector>
64Vector separationImpl(Vector position, std::span<const Vector> neighbors, float radius,
65 float maxAcceleration) {
66 if (!finite(position) || !std::isfinite(radius) || !std::isfinite(maxAcceleration) ||
67 radius <= 0.f || maxAcceleration <= 0.f)
68 return {};
69 double sumX = 0.0;
70 double sumY = 0.0;
71 double sumZ = 0.0;
72 for (const Vector neighbor : neighbors) {
73 if (!finite(neighbor)) continue;
74 const double neighborDistance = distance(position, neighbor);
75 if (neighborDistance > 0.0 && neighborDistance < radius) {
76 const double weight = (radius - neighborDistance) / (radius * neighborDistance);
77 sumX += (static_cast<double>(position.x) - neighbor.x) * weight;
78 sumY += (static_cast<double>(position.y) - neighbor.y) * weight;
79 if constexpr (requires { position.z; })
80 sumZ += (static_cast<double>(position.z) - neighbor.z) * weight;
81 }
82 }
83 const double sumLength = std::hypot(sumX, sumY, sumZ);
84 const double scale = sumLength > maxAcceleration ? maxAcceleration / sumLength : 1.0;
85 if constexpr (requires { position.z; })
86 return {static_cast<float>(sumX * scale), static_cast<float>(sumY * scale),
87 static_cast<float>(sumZ * scale)};
88 return {static_cast<float>(sumX * scale), static_cast<float>(sumY * scale)};
89}
90
91template <typename Vector>
92int pathTargetImpl(Vector position, std::span<const Vector> points, int current,
93 float tolerance) {
94 if (!finite(position) || points.empty()) return -1;
95 if (std::ranges::any_of(points, [](Vector point) { return !finite(point); })) return -1;
96 current = std::clamp(current, 0, static_cast<int>(points.size() - 1));
97 const float acceptedTolerance =
98 std::isfinite(tolerance) ? std::max(0.f, tolerance) : 0.f;
99 while (current + 1 < static_cast<int>(points.size()) &&
100 distance(points[current], position) <= acceptedTolerance)
101 ++current;
102 return current;
103}
104
105template <typename Vector>
106Vector avoidImpl(Vector position, Vector velocity, Vector obstacle, float obstacleRadius,
107 float lookAhead, float maxAcceleration) {
108 if (!finite(position) || !finite(velocity) || !finite(obstacle) ||
109 !std::isfinite(obstacleRadius) || !std::isfinite(lookAhead) ||
110 !std::isfinite(maxAcceleration) || obstacleRadius <= 0.f || lookAhead < 0.f ||
111 maxAcceleration <= 0.f)
112 return {};
113 const Vector offset = scaledDirection(Vector{}, velocity, lookAhead);
114 const double predictedX = static_cast<double>(position.x) + offset.x;
115 const double predictedY = static_cast<double>(position.y) + offset.y;
116 const double predictedZ = [&] {
117 if constexpr (requires { position.z; }) return static_cast<double>(position.z) + offset.z;
118 return 0.0;
119 }();
120 const double overlapX = predictedX - obstacle.x;
121 const double overlapY = predictedY - obstacle.y;
122 const double overlapZ = [&] {
123 if constexpr (requires { obstacle.z; }) return predictedZ - obstacle.z;
124 return 0.0;
125 }();
126 const double overlapLength = std::hypot(overlapX, overlapY, overlapZ);
127 if (overlapLength > obstacleRadius) return {};
128 if (overlapLength > 0.0) {
129 if constexpr (requires { position.z; })
130 return {static_cast<float>(overlapX / overlapLength * maxAcceleration),
131 static_cast<float>(overlapY / overlapLength * maxAcceleration),
132 static_cast<float>(overlapZ / overlapLength * maxAcceleration)};
133 return {static_cast<float>(overlapX / overlapLength * maxAcceleration),
134 static_cast<float>(overlapY / overlapLength * maxAcceleration)};
135 }
136 const Vector oppositeVelocity = [&] {
137 if constexpr (requires { velocity.z; })
138 return Vector{-velocity.x, -velocity.y, -velocity.z};
139 return Vector{-velocity.x, -velocity.y};
140 }();
141 const Vector fallback = scaledDirection(Vector{}, oppositeVelocity, maxAcceleration);
142 if (distance(Vector{}, fallback) > 0.0) return fallback;
143 if constexpr (requires { position.z; }) return Vector{maxAcceleration, 0.f, 0.f};
144 return Vector{maxAcceleration, 0.f};
145}
146
147} // namespace
148
150 return seekImpl(position, target, maxSpeed);
151}
153 return seekImpl(position, target, maxSpeed);
154}
156 return seekImpl(target, position, maxSpeed);
157}
159 return seekImpl(target, position, maxSpeed);
160}
161Vector2 arrive(Vector2 position, Vector2 target, float maxSpeed, float slowRadius,
162 float stopRadius) {
163 return arriveImpl(position, target, maxSpeed, slowRadius, stopRadius);
164}
165Vector3 arrive(Vector3 position, Vector3 target, float maxSpeed, float slowRadius,
166 float stopRadius) {
167 return arriveImpl(position, target, maxSpeed, slowRadius, stopRadius);
168}
169Vector2 separation(Vector2 position, std::span<const Vector2> neighbors, float radius,
170 float maxAcceleration) {
171 return separationImpl(position, neighbors, radius, maxAcceleration);
172}
173Vector3 separation(Vector3 position, std::span<const Vector3> neighbors, float radius,
174 float maxAcceleration) {
175 return separationImpl(position, neighbors, radius, maxAcceleration);
176}
177int pathTarget(Vector2 position, std::span<const Vector2> points, int current,
178 float tolerance) {
179 return pathTargetImpl(position, points, current, tolerance);
180}
181int pathTarget(Vector3 position, std::span<const Vector3> points, int current,
182 float tolerance) {
183 return pathTargetImpl(position, points, current, tolerance);
184}
185Vector2 avoid(Vector2 position, Vector2 velocity, Vector2 obstacle, float obstacleRadius,
186 float lookAhead, float maxAcceleration) {
187 return avoidImpl(position, velocity, obstacle, obstacleRadius, lookAhead, maxAcceleration);
188}
189Vector3 avoid(Vector3 position, Vector3 velocity, Vector3 obstacle, float obstacleRadius,
190 float lookAhead, float maxAcceleration) {
191 return avoidImpl(position, velocity, obstacle, obstacleRadius, lookAhead, maxAcceleration);
192}
193
194} // namespace eve::math::steering
LogicalId target
double value
std::string from
float length
Definition CaveMesh.cpp:94
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
size_t offset
std::array< float, 3 > position
std::array< float, 3 > scale
float distance
bool finite
float radius
std::shared_ptr< const std::vector< glm::vec2 > > points
double current
float dz
float dy
float dx
float separation
Definition TreeMesh.cpp:159
glm::vec3 point
Vector2 arrive(Vector2 position, Vector2 target, float maxSpeed, float slowRadius, float stopRadius)
Slows toward target inside slowRadius and stops inside stopRadius.
Definition Steering.cpp:161
Vector2 seek(Vector2 position, Vector2 target, float maxSpeed)
Returns a velocity of at most maxSpeed directed from position to target.
Definition Steering.cpp:149
Vector2 avoid(Vector2 position, Vector2 velocity, Vector2 obstacle, float obstacleRadius, float lookAhead, float maxAcceleration)
Computes avoidance acceleration away from a predicted obstacle overlap.
Definition Steering.cpp:185
Vector2 flee(Vector2 position, Vector2 target, float maxSpeed)
Returns a velocity of at most maxSpeed directed away from target.
Definition Steering.cpp:155
int pathTarget(Vector2 position, std::span< const Vector2 > points, int current, float tolerance)
Selects the current path point, advancing across points within tolerance.
Definition Steering.cpp:177
Stateless steering vector algorithms shared by 2D and 3D callers.
Definition Steering.h:17
Plain 3D value used by the stateless steering calculations.
Definition Steering.h:23