mirror of
https://github.com/RawAccelOfficial/rawaccel.git
synced 2025-05-13 03:17:04 +02:00
296 lines
9.7 KiB
C++
296 lines
9.7 KiB
C++
#pragma once
|
|
|
|
#define _USE_MATH_DEFINES
|
|
#include <math.h>
|
|
|
|
#include "rawaccel-settings.h"
|
|
#include "x64-util.hpp"
|
|
|
|
#include "accel-linear.hpp"
|
|
#include "accel-classic.hpp"
|
|
#include "accel-natural.hpp"
|
|
#include "accel-naturalgain.hpp"
|
|
#include "accel-power.hpp"
|
|
#include "accel-motivity.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 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
|
|
};
|
|
}
|
|
|
|
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 = 128;
|
|
|
|
/// <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) {
|
|
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;
|
|
};
|
|
|
|
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::naturalgain: return vis(var.u.naturalgain);
|
|
case accel_mode::power: return vis(var.u.power);
|
|
case accel_mode::motivity: return vis(var.u.motivity);
|
|
default: return vis(var.u.noaccel);
|
|
}
|
|
}
|
|
|
|
struct accel_variant {
|
|
si_pair* lookup;
|
|
|
|
accel_mode tag = accel_mode::noaccel;
|
|
|
|
union union_t {
|
|
accel_linear linear;
|
|
accel_classic classic;
|
|
accel_natural natural;
|
|
accel_naturalgain naturalgain;
|
|
accel_power power;
|
|
accel_motivity motivity;
|
|
accel_noaccel noaccel = {};
|
|
} u = {};
|
|
|
|
accel_variant(const accel_args& args, accel_mode mode, si_pair* lut = nullptr) :
|
|
tag(mode), lookup(lut)
|
|
{
|
|
visit_accel([&](auto& impl) {
|
|
impl = { args };
|
|
}, *this);
|
|
|
|
if (lookup && tag == accel_mode::motivity) {
|
|
u.motivity.fn.fill(lookup);
|
|
}
|
|
|
|
}
|
|
|
|
inline double apply(double speed) const {
|
|
if (lookup && tag == accel_mode::motivity) {
|
|
return u.motivity.fn.apply(lookup, speed);
|
|
}
|
|
|
|
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 {
|
|
|
|
// <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>
|
|
/// 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="accel"> The accel implementation used in the containing accel_variant </param>
|
|
velocity_gain_cap(double speed, const accel_variant& 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;
|
|
|
|
// Find the corresponding output velocities for the two points.
|
|
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;
|
|
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 apply(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 threshold > 0 && speed > threshold;
|
|
}
|
|
|
|
velocity_gain_cap() = default;
|
|
};
|
|
|
|
struct accelerator {
|
|
accel_variant accel;
|
|
velocity_gain_cap gain_cap;
|
|
accel_scale_clamp clamp;
|
|
double output_speed_cap = 0;
|
|
|
|
accelerator(const accel_args& args, accel_mode mode, si_pair* lut = nullptr) :
|
|
accel(args, mode, lut), gain_cap(args.gain_cap, accel), clamp(args.scale_cap)
|
|
{
|
|
output_speed_cap = maxsd(args.speed_cap, 0);
|
|
}
|
|
|
|
inline double apply(double speed) const {
|
|
double scale;
|
|
|
|
if (gain_cap.should_apply(speed)) {
|
|
scale = gain_cap.apply(speed);
|
|
}
|
|
else {
|
|
scale = accel.apply(speed);
|
|
}
|
|
|
|
scale = clamp(scale);
|
|
|
|
if (output_speed_cap > 0 && (scale * speed) > output_speed_cap) {
|
|
scale = output_speed_cap / speed;
|
|
}
|
|
|
|
return scale;
|
|
}
|
|
|
|
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;
|
|
vec2<accelerator> accels;
|
|
vec2d sensitivity = { 1, 1 };
|
|
|
|
mouse_modifier(const settings& args, vec2<si_pair*> luts = {}) :
|
|
combine_magnitudes(args.combine_mags)
|
|
{
|
|
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 ((combine_magnitudes && args.modes.x == accel_mode::noaccel) ||
|
|
(args.modes.x == accel_mode::noaccel &&
|
|
args.modes.y == accel_mode::noaccel)) {
|
|
return;
|
|
}
|
|
|
|
accels.x = accelerator(args.argsv.x, args.modes.x, luts.x);
|
|
accels.y = accelerator(args.argsv.y, args.modes.y, luts.y);
|
|
apply_accel = true;
|
|
}
|
|
|
|
void modify(vec2d& movement, milliseconds time) {
|
|
apply_rotation(movement);
|
|
apply_acceleration(movement, [=] { return time; });
|
|
apply_sensitivity(movement);
|
|
}
|
|
|
|
inline void apply_rotation(vec2d& movement) {
|
|
if (apply_rotate) movement = rotate.apply(movement);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void apply_sensitivity(vec2d& movement) {
|
|
movement.x *= sensitivity.x;
|
|
movement.y *= sensitivity.y;
|
|
}
|
|
|
|
mouse_modifier() = default;
|
|
};
|
|
|
|
} // rawaccel
|