summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/rawaccel.hpp106
1 files changed, 99 insertions, 7 deletions
diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp
index 7aa1eb2..fc06731 100644
--- a/common/rawaccel.hpp
+++ b/common/rawaccel.hpp
@@ -78,11 +78,89 @@ namespace rawaccel {
/// <summary> Tagged union to hold all accel implementations and allow "polymorphism" via a visitor call. </summary>
using accel_impl_t = tagged_union<accel_linear, accel_classic, accel_natural, accel_logarithmic, accel_sigmoid, accel_power, accel_noaccel>;
+ /// <summary> Struct to hold information about applying a gain cap. </summary>
+ struct velocity_gain_cap {
+
+ // <summary> The minimum speed past which gain cap is applied. </summary>
+ double threshold = 0;
+
+ // <summary> The gain at the point of cap </summary>
+ double slope = 0;
+
+ // <summary> The intercept for the line with above slope to give continuous velocity function </summary>
+ double intercept = 0;
+
+ // <summary> Whether or not velocity gain cap is enabled </summary>
+ bool cap_gain_enabled = false;
+
+ /// <summary>
+ /// Initializes a velocity gain cap for a certain speed threshold
+ /// by estimating the slope at the threshold and creating a line
+ /// with that slope for output velocity calculations.
+ /// </summary>
+ /// <param name="speed"> The speed at which velocity gain cap will kick in </param>
+ /// <param name="offset"> The offset applied in accel calculations </param>
+ /// <param name="accel"> The accel implementation used in the containing accel_fn </param>
+ velocity_gain_cap(double speed, double offset, accel_impl_t accel)
+ {
+ if (speed <= 0) return;
+
+ // Estimate gain at cap point by taking line between two input vs output velocity points.
+ // First input velocity point is at cap; for second pick a velocity a tiny bit larger.
+ double speed_second = 1.001 * speed;
+ double speed_diff = speed_second - speed;
+
+ // Return if by glitch or strange values the difference in points is 0.
+ if (speed_diff == 0) return;
+
+ cap_gain_enabled = true;
+
+ // Find the corresponding output velocities for the two points.
+ // Subtract offset for acceleration, like in accel_fn()
+ double out_first = accel.visit([=](auto&& impl) {
+ double accel_val = impl.accelerate(speed-offset);
+ return impl.scale(accel_val);
+ }).x * speed;
+ double out_second = accel.visit([=](auto&& impl) {
+ double accel_val = impl.accelerate(speed_second-offset);
+ return impl.scale(accel_val);
+ }).x * speed_second;
+
+ // Calculate slope and intercept from two points.
+ slope = (out_second - out_first) / speed_diff;
+ intercept = out_first - slope * speed;
+
+ threshold = speed;
+ }
+
+ /// <summary>
+ /// Applies velocity gain cap to speed.
+ /// Returns scale value by which to multiply input to place on gain cap line.
+ /// </summary>
+ /// <param name="speed"> Speed to be capped </param>
+ /// <returns> Scale multiplier for input </returns>
+ inline double operator()(double speed) const {
+ return slope + intercept / speed;
+ }
+
+ /// <summary>
+ /// Whether gain cap should be applied to given speed.
+ /// </summary>
+ /// <param name="speed"> Speed to check against threshold. </param>
+ /// <returns> Whether gain cap should be applied. </returns>
+ inline bool should_apply(double speed) const {
+ return cap_gain_enabled && speed > threshold;
+ }
+
+ velocity_gain_cap() = default;
+ };
+
struct accel_fn_args {
accel_args acc_args;
int accel_mode = accel_impl_t::id<accel_noaccel>;
milliseconds time_min = 0.4;
vec2d cap = { 0, 0 };
+ double gain_cap = 0;
};
/// <summary> Struct for holding acceleration application details. </summary>
@@ -105,17 +183,20 @@ namespace rawaccel {
/// <summary> The object which sets a min and max for the acceleration scale. </summary>
vec2<accel_scale_clamp> clamp;
+ velocity_gain_cap gain_cap = velocity_gain_cap();
+
accel_function(const accel_fn_args& args) {
if (args.time_min <= 0) bad_arg("min time must be positive");
if (args.acc_args.offset < 0) bad_arg("offset must not be negative");
accel.tag = args.accel_mode;
- accel.visit([&](auto& impl){ impl = { args.acc_args }; });
+ accel.visit([&](auto& impl) { impl = { args.acc_args }; });
time_min = args.time_min;
speed_offset = args.acc_args.offset;
clamp.x = accel_scale_clamp(args.cap.x);
clamp.y = accel_scale_clamp(args.cap.y);
+ gain_cap = velocity_gain_cap(args.gain_cap, speed_offset, accel);
}
/// <summary>
@@ -127,12 +208,23 @@ namespace rawaccel {
inline vec2d operator()(const vec2d& input, milliseconds time) const {
double mag = sqrtsd(input.x * input.x + input.y * input.y);
double time_clamped = clampsd(time, time_min, 100);
- double speed = maxsd(mag / time_clamped - speed_offset, 0);
-
- vec2d scale = accel.visit([=](auto&& impl) {
- double accel_val = impl.accelerate(speed);
- return impl.scale(accel_val);
- });
+ double raw_speed = mag / time_clamped;
+
+ vec2d scale;
+
+ // gain_cap needs raw speed for velocity line calculation
+ if (gain_cap.should_apply(raw_speed))
+ {
+ double gain_cap_scale = gain_cap(raw_speed);
+ scale = { gain_cap_scale, gain_cap_scale };
+ }
+ else
+ {
+ scale = accel.visit([=](auto&& impl) {
+ double accel_val = impl.accelerate(maxsd(mag / time_clamped - speed_offset, 0));
+ return impl.scale(accel_val);
+ });
+ }
return {
input.x * clamp.x(scale.x),