41template <
size_t N,
typename T>
44 static_assert(
N >= 1,
"backward_difference<N> requires N >= 1; N = 0 is the sample itself");
46 using S = scalar_t<T>;
49 double binomial = 1.0;
51 for (
size_t k = 1;
k <=
N; ++
k) {
52 binomial = binomial *
static_cast<double>(
N -
k + 1) /
static_cast<double>(
k);
53 const double sign = (
k % 2 == 0) ? 1.0 : -1.0;
54 acc = acc +
static_cast<S
>(sign * binomial) * history[
k];
58 for (
size_t i = 0; i <
N; ++i)
61 return acc /
static_cast<S
>(denom);
81template <
size_t N,
typename T>
84 static_assert(
N >= 1,
"forward_difference<N> requires N >= 1");
86 using S = scalar_t<T>;
89 double binomial = 1.0;
91 for (
size_t k = 0;
k <=
N; ++
k) {
93 binomial = binomial *
static_cast<double>(
N -
k + 1) /
static_cast<double>(
k);
94 const double sign = ((
N -
k) % 2 == 0) ? 1.0 : -1.0;
95 acc = acc +
static_cast<S
>(sign * binomial) * history[
k];
99 for (
size_t i = 0; i <
N; ++i)
102 return acc /
static_cast<S
>(denom);
119 using S = scalar_t<T>;
120 return (history[0] - history[2]) /
static_cast<S
>(2.0 * dt);
138 return backward_difference<2>(history, dt);
149 return backward_difference<1>(history, dt);
156 return backward_difference<2>(history, dt);
169 return backward_difference<3>(history, dt);
176 return backward_difference<4>(history, dt);
183 return backward_difference<5>(history, dt);
190 return backward_difference<6>(history, dt);
215 using S = scalar_t<T>;
216 window = window < 1 ? 1 : window;
217 const auto s_dt =
static_cast<S
>(dt);
218 T acc = (history[0] - history[1]) / s_dt;
219 for (
size_t i = 1; i < window; ++i)
220 acc = acc + (history[i] - history[i + 1]) / s_dt;
221 return acc /
static_cast<S
>(window);
240 using S = scalar_t<T>;
241 window = window < 1 ? 1 : window;
243 for (
size_t i = 1; i < window; ++i)
244 acc = acc + history[i];
245 return acc /
static_cast<S
>(window);
260 return std::abs(
velocity(history, dt));
271 const double v =
velocity(history, dt);
272 return (v > 0.0) ? 1.0 : ((v < 0.0) ? -1.0 : 0.0);
287 return glm::length(
velocity(history, dt));
298 return glm::length(
velocity(history, dt));
310 const glm::vec2 v =
velocity(history, dt);
311 const float len = glm::length(v);
312 return (len > 1e-6F) ? (v / len) : glm::vec2(0.0F);
324 const glm::vec3 v =
velocity(history, dt);
325 const float len = glm::length(v);
326 return (len > 1e-6F) ? (v / len) : glm::vec3(0.0F);
338[[nodiscard]]
inline float heading(
const glm::vec2& v)
noexcept
340 return std::atan2(v.y, v.x);
355 float delta = to - from;
356 const float two_pi = 2.0F * std::numbers::pi_v<float>;
357 delta = std::fmod(delta + std::numbers::pi_v<float>, two_pi);
360 return delta - std::numbers::pi_v<float>;
377 return angular_delta(headings[0], headings[1]) /
static_cast<float>(dt);
386[[nodiscard]]
inline float cross_2d(
const glm::vec2&
a,
const glm::vec2&
b)
noexcept
388 return a.x *
b.y -
a.y *
b.x;
418[[nodiscard]]
inline float curvature(
const glm::vec2& vel,
const glm::vec2& accel,
float min_speed = 1e-3F) noexcept
420 const float speed_sq = glm::dot(vel, vel);
421 const float spd = std::sqrt(speed_sq);
424 return cross_2d(vel, accel) / (speed_sq * spd);
455 window = window < 2 ? 2 : window;
457 for (
size_t i = 0; i + 1 < window; ++i)
458 len += glm::length(history[i] - history[i + 1]);
470 window = window < 2 ? 2 : window;
471 return glm::length(history[0] - history[window - 1]);
506 window = window < 3 ? 3 : window;
508 float prev_heading =
heading(history[0] - history[1]);
509 for (
size_t i = 1; i + 1 < window; ++i) {
510 const float h =
heading(history[i] - history[i + 1]);
541 const glm::vec2 c =
centroid(history, window);
542 float max_dist = 0.0F;
543 for (
size_t i = 0; i < window; ++i)
544 max_dist = std::max(max_dist, glm::length(history[i] - c));
581 for (
size_t i = samples.size(); i-- > 0;)
582 history.
push(samples[i]);
587template <
size_t N,
typename T>
590 return backward_difference<N>(
to_history(samples), dt);
594template <
size_t N,
typename T>
597 return forward_difference<N>(
to_history(samples), dt);
616[[nodiscard]]
inline T
velocity(std::span<const T> samples,
double dt)
noexcept
623[[nodiscard]]
inline T
acceleration(std::span<const T> samples,
double dt)
noexcept
630[[nodiscard]]
inline T
jerk(std::span<const T> samples,
double dt)
noexcept
637[[nodiscard]]
inline T
jounce(std::span<const T> samples,
double dt)
noexcept
644[[nodiscard]]
inline T
crackle(std::span<const T> samples,
double dt)
noexcept
651[[nodiscard]]
inline T
pop(std::span<const T> samples,
double dt)
noexcept
658[[nodiscard]]
inline T
smoothed_velocity(std::span<const T> samples,
double dt,
size_t window)
noexcept
665[[nodiscard]]
inline T
moving_average(std::span<const T> samples,
size_t window)
noexcept
671[[nodiscard]]
inline double speed(std::span<const double> samples,
double dt)
noexcept
677[[nodiscard]]
inline double direction_sign(std::span<const double> samples,
double dt)
noexcept
683[[nodiscard]]
inline float speed(std::span<const glm::vec2> samples,
double dt)
noexcept
689[[nodiscard]]
inline float speed(std::span<const glm::vec3> samples,
double dt)
noexcept
695[[nodiscard]]
inline glm::vec2
heading_vector(std::span<const glm::vec2> samples,
double dt)
noexcept
701[[nodiscard]]
inline glm::vec3
heading_vector(std::span<const glm::vec3> samples,
double dt)
noexcept
707[[nodiscard]]
inline float angular_velocity(std::span<const float> headings,
double dt)
noexcept
713[[nodiscard]]
inline float curvature_from_history(std::span<const glm::vec2> samples,
double dt,
float min_speed = 1e-3F) noexcept
719[[nodiscard]]
inline float path_length(std::span<const glm::vec2> samples,
size_t window)
noexcept
725[[nodiscard]]
inline float net_displacement(std::span<const glm::vec2> samples,
size_t window)
noexcept
731[[nodiscard]]
inline float straightness(std::span<const glm::vec2> samples,
size_t window)
noexcept
737[[nodiscard]]
inline float total_turning(std::span<const glm::vec2> samples,
size_t window)
noexcept
743[[nodiscard]]
inline glm::vec2
centroid(std::span<const glm::vec2> samples,
size_t window)
noexcept
749[[nodiscard]]
inline float spread_radius(std::span<const glm::vec2> samples,
size_t window)
noexcept
#define N(method_name, full_type_name)
void push(const T &value)
Push new value to front of history.
History buffer for difference equations and recursive relations.
double direction_sign(const Memory::HistoryBuffer< double > &history, double dt) noexcept
Sign of a scalar first difference.
T acceleration(const Memory::HistoryBuffer< T > &history, double dt) noexcept
Second difference: rate of change of velocity.
T forward_difference(const Memory::HistoryBuffer< T > &history, double dt) noexcept
N-th order forward finite difference, expressed on a HistoryBuffer.
T moving_average(const Memory::HistoryBuffer< T > &history, size_t window) noexcept
Simple moving average over the newest window samples.
glm::vec2 centroid(const Memory::HistoryBuffer< glm::vec2 > &history, size_t window) noexcept
Mean position across a window.
float angular_delta(float to, float from) noexcept
Shortest signed angular difference between two headings.
T velocity(const Memory::HistoryBuffer< T > &history, double dt) noexcept
First difference: rate of change of position.
float spread_radius(const Memory::HistoryBuffer< glm::vec2 > &history, size_t window) noexcept
Bounding radius of a window around its centroid.
float curvature_from_history(const Memory::HistoryBuffer< glm::vec2 > &history, double dt, float min_speed=1e-3F) noexcept
Signed curvature computed directly from a position HistoryBuffer.
T smoothed_velocity(const Memory::HistoryBuffer< T > &history, double dt, size_t window) noexcept
Velocity averaged over a short window rather than a single interval.
float cross_2d(const glm::vec2 &a, const glm::vec2 &b) noexcept
2D scalar cross product, sign of turn direction
float curvature(const glm::vec2 &vel, const glm::vec2 &accel, float min_speed=1e-3F) noexcept
Signed curvature from velocity and acceleration.
T central_first_derivative(const Memory::HistoryBuffer< T > &history, double dt) noexcept
Central difference approximation of the first derivative.
float straightness(const Memory::HistoryBuffer< glm::vec2 > &history, size_t window) noexcept
Straightness of a path over a window, 1.0 is a straight line.
float path_length(const Memory::HistoryBuffer< glm::vec2 > &history, size_t window) noexcept
Path length accumulated across the newest window positions.
T crackle(const Memory::HistoryBuffer< T > &history, double dt) noexcept
Fifth difference: rate of change of jounce.
T pop(const Memory::HistoryBuffer< T > &history, double dt) noexcept
Sixth difference: rate of change of crackle.
double speed(const Memory::HistoryBuffer< double > &history, double dt) noexcept
Absolute value of a scalar first difference.
T central_second_derivative(const Memory::HistoryBuffer< T > &history, double dt) noexcept
Central difference approximation of the second derivative.
Memory::HistoryBuffer< T > to_history(std::span< const T > samples) noexcept
Build a HistoryBuffer<T> from a newest-first span.
float total_turning(const Memory::HistoryBuffer< glm::vec2 > &history, size_t window) noexcept
Total absolute turning accumulated across a window.
float net_displacement(const Memory::HistoryBuffer< glm::vec2 > &history, size_t window) noexcept
Net displacement across the newest window positions.
float heading(const glm::vec2 &v) noexcept
Heading angle of a 2D vector.
float angular_velocity(const Memory::HistoryBuffer< float > &headings, double dt) noexcept
Angular velocity from consecutive headings in a HistoryBuffer.
T backward_difference(const Memory::HistoryBuffer< T > &history, double dt) noexcept
N-th order backward finite difference of a HistoryBuffer, scaled by dt^N.
T jounce(const Memory::HistoryBuffer< T > &history, double dt) noexcept
Fourth difference: rate of change of jerk.
T jerk(const Memory::HistoryBuffer< T > &history, double dt) noexcept
Third difference: rate of change of acceleration.
glm::vec2 heading_vector(const Memory::HistoryBuffer< glm::vec2 > &history, double dt) noexcept
Unit direction of a vec2 first difference.