diff options
Diffstat (limited to 'common/rawaccel.hpp')
| -rw-r--r-- | common/rawaccel.hpp | 266 |
1 files changed, 114 insertions, 152 deletions
diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index 23a8214..d3a2a03 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -3,8 +3,8 @@ #define _USE_MATH_DEFINES #include <math.h> +#include "rawaccel-settings.h" #include "x64-util.hpp" -#include "tagged-union-single.h" #include "accel-linear.hpp" #include "accel-classic.hpp" @@ -31,7 +31,7 @@ namespace rawaccel { /// </summary> /// <param name="input">Input vector to be rotated</param> /// <returns>2d vector of rotated input.</returns> - inline vec2d operator()(const vec2d& input) const { + inline vec2d apply(const vec2d& input) const { return { input.x * rot_vec.x - input.y * rot_vec.y, input.x * rot_vec.y + input.y * rot_vec.x @@ -60,7 +60,7 @@ namespace rawaccel { return clampsd(scale, lo, hi); } - accel_scale_clamp(double cap) : accel_scale_clamp() { + accel_scale_clamp(double cap) { if (cap <= 0) { // use default, effectively uncapped accel return; @@ -76,9 +76,53 @@ namespace rawaccel { accel_scale_clamp() = default; }; + + template <typename Visitor, typename Variant> + inline auto visit_accel(Visitor vis, Variant&& var) { + switch (var.tag) { + case accel_mode::linear: return vis(var.u.linear); + case accel_mode::classic: return vis(var.u.classic); + case accel_mode::natural: return vis(var.u.natural); + case accel_mode::logarithmic: return vis(var.u.logarithmic); + case accel_mode::sigmoid: return vis(var.u.sigmoid); + case accel_mode::naturalgain: return vis(var.u.naturalgain); + case accel_mode::sigmoidgain: return vis(var.u.sigmoidgain); + case accel_mode::power: return vis(var.u.power); + default: return vis(var.u.noaccel); + } + } + + struct accel_variant { + accel_mode tag = accel_mode::noaccel; + + union union_t { + accel_linear linear; + accel_classic classic; + accel_natural natural; + accel_logarithmic logarithmic; + accel_sigmoid sigmoid; + accel_naturalgain naturalgain; + accel_sigmoidgain sigmoidgain; + accel_power power; + accel_noaccel noaccel = {}; + } u = {}; + + accel_variant(const accel_args& args, accel_mode mode) : + tag(mode) + { + visit_accel([&](auto& impl) { + impl = { args }; + }, *this); + } - /// <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_naturalgain, accel_sigmoidgain, accel_noaccel>; + inline double apply(double speed) const { + return visit_accel([=](auto&& impl) { + return impl(speed); + }, *this); + } + + accel_variant() = default; + }; /// <summary> Struct to hold information about applying a gain cap. </summary> struct velocity_gain_cap { @@ -92,18 +136,14 @@ namespace rawaccel { // <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) + /// <param name="accel"> The accel implementation used in the containing accel_variant </param> + velocity_gain_cap(double speed, const accel_variant& accel) { if (speed <= 0) return; @@ -115,18 +155,9 @@ namespace rawaccel { // 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; + double out_first = accel.apply(speed) * speed; + double out_second = accel.apply(speed_second) * speed_second; // Calculate slope and intercept from two points. slope = (out_second - out_first) / speed_diff; @@ -141,7 +172,7 @@ namespace rawaccel { /// </summary> /// <param name="speed"> Speed to be capped </param> /// <returns> Scale multiplier for input </returns> - inline double operator()(double speed) const { + inline double apply(double speed) const { return slope + intercept / speed; } @@ -151,166 +182,97 @@ namespace rawaccel { /// <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; + return threshold > 0 && 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 }; - }; - - /// <summary> Struct for holding acceleration application details. </summary> - struct accel_function { - - /* - This value is ideally a few microseconds lower than - the user's mouse polling interval, though it should - not matter if the system is stable. - */ - /// <summary> The minimum time period for one mouse movement. </summary> - milliseconds time_min = 0.4; - - /// <summary> The offset past which acceleration is applied. </summary> - double speed_offset = 0; + struct accelerator { + accel_variant accel; + velocity_gain_cap gain_cap; + accel_scale_clamp clamp; - /// <summary> The acceleration implementation (i.e. curve) </summary> - accel_impl_t accel; + accelerator(const accel_args& args, accel_mode mode) : + accel(args, mode), gain_cap(args.gain_cap, accel), clamp(args.scale_cap) + {} - /// <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_args impl_args; - - 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 }; }); - impl_args = 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.acc_args.gain_cap, speed_offset, accel); - } - - /// <summary> - /// Applies weighted acceleration to given input for given time period. - /// </summary> - /// <param name="input">2d vector of {x, y} mouse movement to be accelerated</param> - /// <param name="time">Time period over which input movement was accumulated</param> - /// <returns></returns> - 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 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 }; + inline double apply(double speed) const { + if (gain_cap.should_apply(speed)) { + return clamp(gain_cap.apply(speed)); } - 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), - input.y * clamp.y(scale.y) - }; + else return clamp(accel.apply(speed)); } - accel_function() = default; - }; - - struct modifier_args { - double degrees = 0; - vec2d sens = { 1, 1 }; - accel_fn_args acc_fn_args; + accelerator() = default; }; /// <summary> Struct to hold variables and methods for modifying mouse input </summary> struct mouse_modifier { bool apply_rotate = false; bool apply_accel = false; + bool combine_magnitudes = true; rotator rotate; - accel_function accel_fn; + vec2<accelerator> accels; vec2d sensitivity = { 1, 1 }; - mouse_modifier(const modifier_args& args) - : accel_fn(args.acc_fn_args) + mouse_modifier(const settings& args) : + combine_magnitudes(args.combine_mags) { - apply_rotate = args.degrees != 0; - - if (apply_rotate) rotate = rotator(args.degrees); - else rotate = rotator(); - - apply_accel = args.acc_fn_args.accel_mode != 0 && - args.acc_fn_args.accel_mode != accel_impl_t::id<accel_noaccel>; + if (args.degrees_rotation != 0) { + rotate = rotator(args.degrees_rotation); + apply_rotate = true; + } + + if (args.sens.x != 0) sensitivity.x = args.sens.x; + if (args.sens.y != 0) sensitivity.y = args.sens.y; - if (args.sens.x == 0) sensitivity.x = 1; - else sensitivity.x = args.sens.x; + if ((combine_magnitudes && args.modes.x == accel_mode::noaccel) || + (args.modes.x == accel_mode::noaccel && + args.modes.y == accel_mode::noaccel)) { + return; + } - if (args.sens.y == 0) sensitivity.y = 1; - else sensitivity.y = args.sens.y; + accels.x = accelerator(args.argsv.x, args.modes.x); + accels.y = accelerator(args.argsv.y, args.modes.y); + apply_accel = true; } - /// <summary> - /// Applies modification without acceleration. - /// </summary> - /// <param name="input">Input to be modified.</param> - /// <returns>2d vector of modified input.</returns> - inline vec2d modify_without_accel(vec2d input) - { - if (apply_rotate) - { - input = rotate(input); - } - - input.x *= sensitivity.x; - input.y *= sensitivity.y; + void modify(vec2d& movement, milliseconds time) { + apply_rotation(movement); + apply_acceleration(movement, [=] { return time; }); + apply_sensitivity(movement); + } - return input; + inline void apply_rotation(vec2d& movement) { + if (apply_rotate) movement = rotate.apply(movement); } - /// <summary> - /// Applies modification, including acceleration. - /// </summary> - /// <param name="input">Input to be modified</param> - /// <param name="time">Time period for determining acceleration.</param> - /// <returns>2d vector with modified input.</returns> - inline vec2d modify_with_accel(vec2d input, milliseconds time) - { - if (apply_rotate) - { - input = rotate(input); + template <typename TimeSupplier> + inline void apply_acceleration(vec2d& movement, TimeSupplier time_supp) { + if (apply_accel) { + milliseconds time = time_supp(); + + if (combine_magnitudes) { + double mag = sqrtsd(movement.x * movement.x + movement.y * movement.y); + double speed = mag / time; + double scale = accels.x.apply(speed); + movement.x *= scale; + movement.y *= scale; + } + else { + movement.x *= accels.x.apply(fabs(movement.x) / time); + movement.y *= accels.y.apply(fabs(movement.y) / time); + } } + } - input = accel_fn(input, time); - - input.x *= sensitivity.x; - input.y *= sensitivity.y; - - return input; + inline void apply_sensitivity(vec2d& movement) { + movement.x *= sensitivity.x; + movement.y *= sensitivity.y; } mouse_modifier() = default; }; -} // rawaccel
\ No newline at end of file +} // rawaccel |