#pragma once #define _USE_MATH_DEFINES #include #include "vec2.h" #include "x64-util.hpp" #include "external/tagged-union-single.h" #include "accel_linear.cpp" #include "accel_classic.cpp" #include "accel_natural.cpp" #include "accel_logarithmic.cpp" #include "accel_sigmoid.cpp" #include "accel_power.cpp" #include "accel_noaccel.cpp" namespace rawaccel { /// Struct to hold vector rotation details. struct rotator { /// Rotational vector, which points in the direction of the post-rotation positive x axis. vec2d rot_vec = { 1, 0 }; /// /// Rotates given input vector according to struct's rotational vector. /// /// Input vector to be rotated /// 2d vector of rotated input. 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; }; /// Struct to hold clamp (min and max) details for acceleration application struct accel_scale_clamp { double lo = 0; double hi = 9; /// /// Clamps given input to min at lo, max at hi. /// /// Double to be clamped /// Clamped input as double 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; } accel_scale_clamp() = default; }; /// Tagged union to hold all accel implementations and allow "polymorphism" via a visitor call. using accel_implementation_t = tagged_union; struct accel_fn_args { accel_args acc_args = accel_args{}; int accel_mode = 0; milliseconds time_min = 0.4; vec2d weight = { 1, 1 }; vec2d cap = { 0, 0 }; }; /// Struct for holding acceleration application details. 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. */ /// The minimum time period for one mouse movement. milliseconds time_min = 0.4; /// The offset past which acceleration is applied. double speed_offset = 0; /// The acceleration implementation (i.e. curve) accel_implementation_t accel; /// The weight of acceleration applied in {x, y} dimensions. vec2d weight = { 1, 1 }; /// The object which sets a min and max for the acceleration scale. vec2 clamp; accel_function(accel_fn_args args) { accel.tag = args.accel_mode; accel.visit([&](auto& a){ a = {args.acc_args}; }); // Verification is performed by the accel_implementation object // and therefore must occur after the object has been instantiated verify(args.acc_args); time_min = args.time_min; speed_offset = args.acc_args.offset; weight = args.weight; clamp.x = accel_scale_clamp(args.cap.x); clamp.y = accel_scale_clamp(args.cap.y); } /// /// Applies mouse acceleration to a given speed, via visitor function to accel_implementation_t /// /// Speed from which to determine acceleration /// Acceleration as a ratio magnitudes, as a double double apply(double speed) const { return accel.visit([=](auto accel_t) { return accel_t.accelerate(speed); }); } /// /// Verifies acceleration arguments, via visitor function to accel_implementation_t /// /// Arguments to be verified void verify(accel_args args) const { return accel.visit([=](auto accel_t) { accel_t.verify(args); }); } /// /// Applies weighted acceleration to given input for given time period. /// /// 2d vector of {x, y} mouse movement to be accelerated /// Time period over which input movement was accumulated /// 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); double accel_val = apply(speed); double scale_x = weight.x * accel_val + 1; double scale_y = weight.y * accel_val + 1; return { input.x * clamp.x(scale_x), input.y * clamp.y(scale_y) }; } accel_function() = default; }; struct modifier_args { double degrees = 0; vec2d sens = { 1, 1 }; accel_fn_args acc_fn_args = accel_fn_args{}; }; /// Struct to hold variables and methods for modifying mouse input struct mouse_modifier { bool apply_rotate = false; bool apply_accel = false; rotator rotate; accel_function accel_fn; vec2d sensitivity = { 1, 1 }; mouse_modifier(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_implementation_t::id); if (args.sens.x == 0) args.sens.x = 1; if (args.sens.y == 0) args.sens.y = 1; sensitivity = args.sens; } /// /// Applies modification without acceleration. /// /// Input to be modified. /// 2d vector of modified input. inline vec2d modify_without_accel(vec2d input) { if (apply_rotate) { input = rotate(input); } input.x *= sensitivity.x; input.y *= sensitivity.y; return input; } /// /// Applies modification, including acceleration. /// /// Input to be modified /// Time period for determining acceleration. /// 2d vector with modified input. inline vec2d modify_with_accel(vec2d input, milliseconds time) { if (apply_rotate) { input = rotate(input); } if (apply_accel) { input = accel_fn(input, time); } input.x *= sensitivity.x; input.y *= sensitivity.y; return input; } mouse_modifier() = default; }; } // rawaccel