diff options
Diffstat (limited to 'common/rawaccel.hpp')
| -rw-r--r-- | common/rawaccel.hpp | 350 |
1 files changed, 194 insertions, 156 deletions
diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index 095af76..59a0360 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -3,178 +3,216 @@ #define _USE_MATH_DEFINES #include <math.h> -#include "vec2.h" #include "x64-util.hpp" +#include "external/tagged-union-single.h" + +#include "accel-linear.hpp" +#include "accel-classic.hpp" +#include "accel-natural.hpp" +#include "accel-logarithmic.hpp" +#include "accel-sigmoid.hpp" +#include "accel-power.hpp" +#include "accel-noaccel.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold vector rotation details. </summary> + struct rotator { + + /// <summary> Rotational vector, which points in the direction of the post-rotation positive x axis. </summary> + vec2d rot_vec = { 1, 0 }; + + /// <summary> + /// Rotates given input vector according to struct's rotational vector. + /// </summary> + /// <param name="input">Input vector to be rotated</param> + /// <returns>2d vector of rotated input.</returns> + inline vec2d operator()(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 + }; + } + + rotator(double degrees) { + double rads = degrees * M_PI / 180; + rot_vec = { cos(rads), sin(rads) }; + } + + rotator() = default; + }; + + /// <summary> Struct to hold clamp (min and max) details for acceleration application </summary> + struct accel_scale_clamp { + double lo = 0; + double hi = 9; + + /// <summary> + /// Clamps given input to min at lo, max at hi. + /// </summary> + /// <param name="scale">Double to be clamped</param> + /// <returns>Clamped input as double</returns> + inline double operator()(double scale) const { + return clampsd(scale, lo, hi); + } + + accel_scale_clamp(double cap) : accel_scale_clamp() { + if (cap <= 0) { + // use default, effectively uncapped accel + return; + } + + if (cap < 1) { + // assume negative accel + lo = cap; + hi = 1; + } + else hi = cap; + } -namespace rawaccel { + accel_scale_clamp() = default; + }; -enum class mode { noaccel, linear, classic, natural, logarithmic, sigmoid, power }; + /// <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>; -struct rotator { - vec2d rot_vec = { 1, 0 }; + 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 }; + }; - inline vec2d operator()(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 - }; - } + /// <summary> Struct for holding acceleration application details. </summary> + struct accel_function { - rotator(double degrees) { - double rads = degrees * M_PI / 180; - rot_vec = { cos(rads), sin(rads) }; - } + /* + 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; - rotator() = default; -}; + /// <summary> The offset past which acceleration is applied. </summary> + double speed_offset = 0; -struct accel_scale_clamp { - double lo = 0; - double hi = 9; + /// <summary> The acceleration implementation (i.e. curve) </summary> + accel_impl_t accel; - inline double operator()(double scale) const { - return clampsd(scale, lo, hi); - } + /// <summary> The object which sets a min and max for the acceleration scale. </summary> + vec2<accel_scale_clamp> clamp; - accel_scale_clamp(double cap) : accel_scale_clamp() { - if (cap <= 0) { - // use default, effectively uncapped accel - return; - } + accel_function(const accel_fn_args& args) { + if (args.time_min <= 0) error("min time must be positive"); + if (args.acc_args.offset < 0) error("offset must not be negative"); - if (cap < 1) { - // assume negative accel - lo = cap; - hi = 1; - } - else hi = cap; - } - - accel_scale_clamp() = default; -}; - -void error(const char*); - -struct accel_function { - using milliseconds = double; - - /* - 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. - */ - milliseconds time_min = 0.4; - - double speed_offset = 0; - - // speed midpoint in sigmoid mode - double m = 0; - - // accel ramp rate - double b = 0; - - // the limit for natural and sigmoid modes - // or the exponent for classic and power modes - double k = 1; - - vec2d weight = { 1, 1 }; - vec2<accel_scale_clamp> clamp; - - inline vec2d operator()(const vec2d& input, milliseconds time, mode accel_mode) 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); - - double accel_val = 0; - - switch (accel_mode) { - case mode::linear: accel_val = b * speed; - break; - case mode::classic: accel_val = pow(b * speed, k); - break; - case mode::natural: accel_val = k - (k * exp(-b * speed)); - break; - case mode::logarithmic: accel_val = log(speed * b + 1); - break; - case mode::sigmoid: accel_val = k / (exp(-b * (speed - m)) + 1); - break; - case mode::power: accel_val = (speed_offset > 0 && speed < 1) ? 0 : pow(speed*b, k) - 1; - break; - default: - break; + accel.tag = args.accel_mode; + 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); } - double scale_x = weight.x * accel_val + 1; - double scale_y = weight.y * accel_val + 1; + /// <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 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); + }); + + return { + input.x * clamp.x(scale.x), + input.y * clamp.y(scale.y) + }; + } - return { - input.x * clamp.x(scale_x), - input.y * clamp.y(scale_y) - }; - } + accel_function() = default; + }; - struct args_t { - mode accel_mode = mode::noaccel; - milliseconds time_min = 0.4; - double offset = 0; - double accel = 0; - double lim_exp = 2; - double midpoint = 0; - vec2d weight = { 1, 1 }; - vec2d cap = { 0, 0 }; + struct modifier_args { + double degrees = 0; + vec2d sens = { 1, 1 }; + accel_fn_args acc_fn_args; }; - accel_function(args_t args) { - // Preconditions to guard against division by zero and - // ensure the C math functions can not return NaN or -Inf. - if (args.accel < 0) error("accel can not be negative, use a negative weight to compensate"); - if (args.time_min <= 0) error("min time must be positive"); - if (args.lim_exp <= 1) { - if (args.accel_mode == mode::classic) error("exponent must be greater than 1"); - if (args.accel_mode == mode::power) { if (args.lim_exp <=0 ) error("exponent must be greater than 0"); } - else error("limit must be greater than 1"); + /// <summary> Struct to hold variables and methods for modifying mouse input </summary> + struct mouse_modifier { + bool apply_rotate = false; + bool apply_accel = false; + rotator rotate; + accel_function accel_fn; + vec2d sensitivity = { 1, 1 }; + + mouse_modifier(const modifier_args& args) + : accel_fn(args.acc_fn_args) + { + 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.sens.x == 0) sensitivity.x = 1; + else sensitivity.x = args.sens.x; + + if (args.sens.y == 0) sensitivity.y = 1; + else sensitivity.y = args.sens.y; + } + + /// <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; + + return input; } - time_min = args.time_min; - m = args.midpoint; - b = args.accel; - k = args.lim_exp - 1; - if (args.accel_mode == mode::natural) b /= k; - if (args.accel_mode == mode::power) k++; - - speed_offset = args.offset; - weight = args.weight; - clamp.x = accel_scale_clamp(args.cap.x); - clamp.y = accel_scale_clamp(args.cap.y); - } - - accel_function() = default; -}; - -struct variables { - bool apply_rotate = false; - bool apply_accel = false; - mode accel_mode = mode::noaccel; - rotator rotate; - accel_function accel_fn; - vec2d sensitivity = { 1, 1 }; - - variables(double degrees, vec2d sens, accel_function::args_t accel_args) - : accel_fn(accel_args) - { - apply_rotate = degrees != 0; - if (apply_rotate) rotate = rotator(degrees); - else rotate = rotator(); - - apply_accel = accel_args.accel_mode != mode::noaccel; - accel_mode = accel_args.accel_mode; - - if (sens.x == 0) sens.x = 1; - if (sens.y == 0) sens.y = 1; - sensitivity = sens; - } - - variables() = default; -}; - -} // rawaccel + /// <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); + } + + input = accel_fn(input, time); + + input.x *= sensitivity.x; + input.y *= sensitivity.y; + + return input; + } + + mouse_modifier() = default; + }; + +} // rawaccel
\ No newline at end of file |