summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authora1xd <[email protected]>2021-07-05 23:33:41 -0400
committera1xd <[email protected]>2021-07-05 23:33:41 -0400
commit31efc792f5895d7ef3533390875de3c480add996 (patch)
tree8db5b16a88f50448cb525ba8ae56801985294f63 /common
parentMerge pull request #87 from matthewstrasiotto/streamer_mode (diff)
parentHandle power\exponent correctly in GUI (diff)
downloadrawaccel-31efc792f5895d7ef3533390875de3c480add996.tar.xz
rawaccel-31efc792f5895d7ef3533390875de3c480add996.zip
merge lut2
Diffstat (limited to 'common')
-rw-r--r--common/accel-base.hpp66
-rw-r--r--common/accel-classic.hpp127
-rw-r--r--common/accel-invoke.hpp52
-rw-r--r--common/accel-jump.hpp73
-rw-r--r--common/accel-linear.hpp31
-rw-r--r--common/accel-lookup.hpp306
-rw-r--r--common/accel-motivity.hpp117
-rw-r--r--common/accel-natural.hpp52
-rw-r--r--common/accel-naturalgain.hpp28
-rw-r--r--common/accel-noaccel.hpp6
-rw-r--r--common/accel-power.hpp23
-rw-r--r--common/accel-union.hpp102
-rw-r--r--common/common.vcxitems12
-rw-r--r--common/rawaccel-base.hpp109
-rw-r--r--common/rawaccel-error.hpp27
-rw-r--r--common/rawaccel-io-def.h4
-rw-r--r--common/rawaccel-io.hpp122
-rw-r--r--common/rawaccel-settings.h34
-rw-r--r--common/rawaccel-validate.hpp185
-rw-r--r--common/rawaccel-version.h34
-rw-r--r--common/rawaccel.hpp516
-rw-r--r--common/utility-rawinput.hpp2
-rw-r--r--common/utility.hpp88
-rw-r--r--common/x64-util.hpp30
24 files changed, 1363 insertions, 783 deletions
diff --git a/common/accel-base.hpp b/common/accel-base.hpp
deleted file mode 100644
index 42b3bb1..0000000
--- a/common/accel-base.hpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-
-namespace rawaccel {
-
- /// <summary> Struct to hold arguments for an acceleration function. </summary>
- struct accel_args {
- double offset = 0;
- bool legacy_offset = false;
- double accel = 0;
- double scale = 1;
- double limit = 2;
- double exponent = 2;
- double midpoint = 10;
- double weight = 1;
- double scale_cap = 0;
- double gain_cap = 0;
- double speed_cap = 0;
- };
-
- struct domain_args {
- vec2d domain_weights = { 1, 1 };
- double lp_norm = 2;
- };
-
- template <typename Func>
- struct accel_val_base {
- bool legacy_offset = false;
- double offset = 0;
- double weight = 1;
- Func fn;
-
- accel_val_base(const accel_args& args) : fn(args) {}
-
- };
-
- template <typename Func>
- struct additive_accel : accel_val_base<Func> {
-
- additive_accel(const accel_args& args) : accel_val_base(args) {
- this->legacy_offset = args.legacy_offset;
- this->offset = args.offset;
- this->weight = args.weight;
- }
-
- inline double operator()(double speed) const {
- double offset_speed = speed - this->offset;
- if (offset_speed <= 0) return 1;
- if (this->legacy_offset) return 1 + this->fn.legacy_offset(offset_speed) * this->weight;
- return 1 + this->fn(offset_speed) * this->weight;
- }
- };
-
- template <typename Func>
- struct nonadditive_accel : accel_val_base<Func> {
-
- nonadditive_accel(const accel_args& args) : accel_val_base(args) {
- if (args.weight > 0) this->weight = args.weight;
- }
-
- inline double operator()(double speed) const {
- return this->fn(speed) * this->weight;
- }
-
- };
-
-}
diff --git a/common/accel-classic.hpp b/common/accel-classic.hpp
index 1df888a..4385897 100644
--- a/common/accel-classic.hpp
+++ b/common/accel-classic.hpp
@@ -1,36 +1,105 @@
#pragma once
-#include <math.h>
+#include "rawaccel-base.hpp"
+#include "utility.hpp"
-#include "accel-base.hpp"
+#include <math.h>
+#include <float.h>
namespace rawaccel {
- /// <summary> Struct to hold "classic" (linear raised to power) acceleration implementation. </summary>
- struct classic_impl {
- double accel;
- double power;
- double power_inc;
- double offset;
- double multiplicative_const;
-
- classic_impl(const accel_args& args) :
- accel(args.accel), power(args.exponent - 1), offset(args.offset) {
- multiplicative_const = pow(accel, power);
- power_inc = power + 1;
- }
-
- inline double operator()(double speed) const {
- //f(x) = (mx)^(k-1)
- double base_speed = speed + offset;
- return multiplicative_const * pow(speed, power_inc) / base_speed;
- }
-
- inline double legacy_offset(double speed) const {
- return pow(accel * speed, power);
- }
- };
-
- using accel_classic = additive_accel<classic_impl>;
-
+ /// <summary> Struct to hold "classic" (linear raised to power) acceleration implementation. </summary>
+ struct classic_base {
+ double offset;
+ double power;
+ double accel_raised;
+
+ classic_base(const accel_args& args) :
+ offset(args.offset),
+ power(args.power),
+ accel_raised(pow(args.accel_classic, power - 1)) {}
+
+ double base_fn(double x) const
+ {
+ return accel_raised * pow(x - offset, power) / x;
+ }
+ };
+
+ struct classic_legacy : classic_base {
+ double sens_cap = DBL_MAX;
+ double sign = 1;
+
+ classic_legacy(const accel_args& args) :
+ classic_base(args)
+ {
+ if (args.cap > 0) {
+ sens_cap = args.cap - 1;
+
+ if (sens_cap < 0) {
+ sens_cap = -sens_cap;
+ sign = -sign;
+ }
+ }
+ }
+
+ double operator()(double x) const
+ {
+ if (x <= offset) return 1;
+ return sign * minsd(base_fn(x), sens_cap) + 1;
+ }
+ };
+
+ struct classic : classic_base {
+ vec2d gain_cap = { DBL_MAX, DBL_MAX };
+ double constant = 0;
+ double sign = 1;
+
+ classic(const accel_args& args) :
+ classic_base(args)
+ {
+ if (args.cap > 0) {
+ gain_cap.y = args.cap - 1;
+
+ if (gain_cap.y < 0) {
+ gain_cap.y = -gain_cap.y;
+ sign = -sign;
+ }
+
+ gain_cap.x = gain_inverse(gain_cap.y, args.accel_classic, power, offset);
+ constant = (base_fn(gain_cap.x) - gain_cap.y) * gain_cap.x;
+ }
+ }
+
+ double operator()(double x) const
+ {
+ double output;
+
+ if (x <= offset) return 1;
+
+ if (x < gain_cap.x) {
+ output = base_fn(x);
+ }
+ else {
+ output = constant / x + gain_cap.y;
+ }
+
+ return sign * output + 1;
+ }
+
+ static double gain(double x, double accel, double power, double offset)
+ {
+ return power * pow(accel * (x - offset), power - 1);
+ }
+
+ static double gain_inverse(double y, double accel, double power, double offset)
+ {
+ return (accel * offset + pow(y / power, 1 / (power - 1))) / accel;
+ }
+
+ static double gain_accel(double x, double y, double power, double offset)
+ {
+ return -pow(y / power, 1 / (power - 1)) / (offset - x);
+ }
+ };
+
}
diff --git a/common/accel-invoke.hpp b/common/accel-invoke.hpp
new file mode 100644
index 0000000..f2a95dc
--- /dev/null
+++ b/common/accel-invoke.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "accel-union.hpp"
+
+namespace rawaccel {
+
+ class accel_invoker {
+ using callback_t = double (*)(const accel_union&, double, double);
+
+ callback_t cb = &invoke_impl<accel_noaccel>;
+
+ template <typename T>
+ static double invoke_impl(const accel_union& u, double x, double w)
+ {
+ return apply_weighted(reinterpret_cast<const T&>(u), x, w);
+ }
+
+ public:
+
+ accel_invoker(const accel_args& args)
+ {
+ cb = visit_accel([](auto&& arg) {
+ using T = remove_ref_t<decltype(arg)>;
+
+ if constexpr (is_same_v<T, motivity>) {
+ static_assert(sizeof motivity == sizeof binlog_lut);
+ return &invoke_impl<binlog_lut>;
+ }
+ else {
+ return &invoke_impl<T>;
+ }
+
+ }, make_mode(args), accel_union{});
+ }
+
+ accel_invoker() = default;
+
+ double invoke(const accel_union& u, double x, double weight = 1) const
+ {
+ return (*cb)(u, x, weight);
+ }
+ };
+
+ inline vec2<accel_invoker> invokers(const settings& args)
+ {
+ return {
+ accel_invoker(args.argsv.x),
+ accel_invoker(args.argsv.y)
+ };
+ }
+
+}
diff --git a/common/accel-jump.hpp b/common/accel-jump.hpp
new file mode 100644
index 0000000..198891a
--- /dev/null
+++ b/common/accel-jump.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "rawaccel-base.hpp"
+
+namespace rawaccel {
+
+ struct jump_base {
+ vec2d step;
+ double smooth_rate;
+
+ jump_base(const accel_args& args) :
+ step({ args.offset, args.cap - 1 })
+ {
+ if (args.smooth == 0 || args.offset == 0) {
+ smooth_rate = 0;
+ }
+ else {
+ smooth_rate = 2 * PI / (args.offset * args.smooth);
+ }
+
+ }
+
+ bool is_smooth() const
+ {
+ return smooth_rate != 0;
+ }
+
+ double decay(double x) const
+ {
+ return exp(smooth_rate * (step.x - x));
+ }
+
+ double smooth(double x) const
+ {
+ return step.y / (1 + decay(x));
+ }
+
+ double smooth_antideriv(double x) const
+ {
+ return step.y * (x + log(1 + decay(x)) / smooth_rate);
+ }
+ };
+
+ struct jump_legacy : jump_base {
+ using jump_base::jump_base;
+
+ double operator()(double x) const
+ {
+ if (is_smooth()) return smooth(x) + 1;
+ else if (x < step.x) return 1;
+ else return step.y;
+ }
+ };
+
+ struct jump : jump_base {
+ double C;
+
+ jump(const accel_args& args) :
+ jump_base(args),
+ C(-smooth_antideriv(0)) {}
+
+ double operator()(double x) const
+ {
+ if (x <= 0) return 1;
+
+ if (is_smooth()) return 1 + (smooth_antideriv(x) + C) / x;
+
+ if (x < step.x) return 1;
+ else return 1 + step.y * (x - step.x) / x;
+ }
+ };
+
+}
diff --git a/common/accel-linear.hpp b/common/accel-linear.hpp
deleted file mode 100644
index 2bd57b8..0000000
--- a/common/accel-linear.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include "accel-base.hpp"
-
-namespace rawaccel {
-
- /// <summary> Struct to hold linear acceleration implementation. </summary>
- struct linear_impl {
- double accel;
- double offset;
- double subtractive_const;
- double divisive_const;
-
- linear_impl(const accel_args& args) : accel(args.accel), offset(args.offset) {
- subtractive_const = 2 * accel * offset;
- divisive_const = accel * offset * offset;
- }
-
- inline double operator()(double speed) const {
- double base_speed = speed + offset;
- return accel * base_speed - subtractive_const + divisive_const / base_speed;
- }
-
- inline double legacy_offset(double speed) const {
- return accel * speed;
- }
- };
-
- using accel_linear = additive_accel<linear_impl>;
-
-}
diff --git a/common/accel-lookup.hpp b/common/accel-lookup.hpp
new file mode 100644
index 0000000..99f39e9
--- /dev/null
+++ b/common/accel-lookup.hpp
@@ -0,0 +1,306 @@
+#pragma once
+
+#include "rawaccel-base.hpp"
+#include "utility.hpp"
+
+#include <math.h>
+
+namespace rawaccel {
+
+ struct linear_range {
+ double start;
+ double stop;
+ int num;
+
+ template <typename Func>
+ void for_each(Func fn) const
+ {
+ double interval = (stop - start) / (num - 1);
+ for (int i = 0; i < num; i++) {
+ fn(i * interval + start);
+ }
+ }
+
+ int size() const
+ {
+ return num;
+ }
+ };
+
+
+ // represents the range [2^start, 2^stop], with num - 1
+ // elements linearly spaced between each exponential step
+ struct fp_rep_range {
+ int start;
+ int stop;
+ int num;
+
+ template <typename Func>
+ void for_each(Func fn) const
+ {
+ for (int e = 0; e < stop - start; e++) {
+ double exp_scale = scalbn(1, e + start) / num;
+
+ for (int i = 0; i < num; i++) {
+ fn((i + num) * exp_scale);
+ }
+ }
+
+ fn(scalbn(1, stop));
+ }
+
+ int size() const
+ {
+ return (stop - start) * num + 1;
+ }
+ };
+
+ template <typename Lookup>
+ struct lut_base {
+ enum { capacity = SPACED_LUT_CAPACITY };
+ using value_t = float;
+
+ template <typename Func>
+ void fill(Func fn)
+ {
+ auto* self = static_cast<Lookup*>(this);
+
+ self->range.for_each([&, fn, i = 0](double x) mutable {
+ self->data[i++] = static_cast<value_t>(fn(x));
+ });
+ }
+
+ };
+
+ struct linear_lut : lut_base<linear_lut> {
+ linear_range range;
+ bool transfer = false;
+ value_t data[capacity] = {};
+
+ double operator()(double x) const
+ {
+ if (x > range.start) {
+ double range_dist = range.stop - range.start;
+ double idx_f = (x - range.start) * (range.num - 1) / range_dist;
+
+ unsigned idx = min(static_cast<int>(idx_f), range.size() - 2);
+
+ if (idx < capacity - 1) {
+ double y = lerp(data[idx], data[idx + 1], idx_f - idx);
+ if (transfer) y /= x;
+ return y;
+ }
+ }
+
+ double y = data[0];
+ if (transfer) y /= range.start;
+ return y;
+ }
+
+ linear_lut(const spaced_lut_args& args) :
+ range({
+ args.start,
+ args.stop,
+ args.num_elements
+ }),
+ transfer(args.transfer) {}
+
+ linear_lut(const accel_args& args) :
+ linear_lut(args.spaced_args) {}
+ };
+
+ struct binlog_lut : lut_base<binlog_lut> {
+ fp_rep_range range;
+ double x_start;
+ bool transfer = false;
+ value_t data[capacity] = {};
+
+ double operator()(double x) const
+ {
+ int e = min(ilogb(x), range.stop - 1);
+
+ if (e >= range.start) {
+ int idx_int_log_part = e - range.start;
+ double idx_frac_lin_part = scalbn(x, -e) - 1;
+ double idx_f = range.num * (idx_int_log_part + idx_frac_lin_part);
+
+ unsigned idx = min(static_cast<int>(idx_f), range.size() - 2);
+
+ if (idx < capacity - 1) {
+ double y = lerp(data[idx], data[idx + 1], idx_f - idx);
+ if (transfer) y /= x;
+ return y;
+ }
+ }
+
+ double y = data[0];
+ if (transfer) y /= x_start;
+ return y;
+ }
+
+ binlog_lut(const spaced_lut_args& args) :
+ range({
+ static_cast<int>(args.start),
+ static_cast<int>(args.stop),
+ args.num_elements
+ }),
+ x_start(scalbn(1, range.start)),
+ transfer(args.transfer) {}
+
+ binlog_lut(const accel_args& args) :
+ binlog_lut(args.spaced_args) {}
+ };
+
+ struct si_pair {
+ float slope = 0;
+ float intercept = 0;
+ };
+
+ struct arbitrary_lut_point {
+ float applicable_speed = 0;
+ si_pair slope_intercept = {};
+ };
+
+ struct arbitrary_lut {
+ enum { capacity = ARB_LUT_CAPACITY };
+
+ fp_rep_range range;
+ arbitrary_lut_point data[capacity] = {};
+ int log_lookup[capacity] = {};
+ double first_point_speed;
+ double last_point_speed;
+ int last_arbitrary_index;
+ int last_log_lookup_index;
+ double last_log_lookup_speed;
+ double first_log_lookup_speed;
+ bool velocity_points;
+
+ double operator()(double speed) const
+ {
+ int index = 0;
+ int last_arb_index = last_arbitrary_index;
+ int last_log_index = last_log_lookup_index;
+
+ if (speed <= 0) return 1;
+
+ if (unsigned(last_arb_index) < capacity &&
+ unsigned(last_log_index) < capacity &&
+ speed > first_point_speed)
+ {
+ if (speed > last_point_speed)
+ {
+ index = last_arb_index;
+ }
+ else if (speed > last_log_lookup_speed)
+ {
+ int last_log = log_lookup[last_log_index];
+ if (unsigned(last_log) >= capacity) return 1;
+ index = search_from(last_log, last_arb_index, speed);
+ }
+ else if (speed < first_log_lookup_speed)
+ {
+ index = search_from(0, last_arb_index, speed);
+ }
+ else
+ {
+ int log_index = get_log_index(speed);
+ if (unsigned(log_index) >= capacity) return 1;
+ int arbitrary_index = log_lookup[log_index];
+ if (arbitrary_index < 0) return 1;
+ index = search_from(arbitrary_index, last_arb_index, speed);
+ }
+
+ }
+
+ return apply(index, speed);
+ }
+
+ int inline get_log_index(double speed) const
+ {
+ double speed_log = log(speed) - range.start;
+ int index = (int)floor(speed_log * range.num);
+ return index;
+ }
+
+ int inline search_from(int index, int last, double speed) const
+ {
+ do
+ {
+ index++;
+ }
+ while (index <= last && data[index].applicable_speed < speed);
+
+ return index - 1;
+ }
+
+ double inline apply(int index, double speed) const
+ {
+ auto [slope, intercept] = data[index].slope_intercept;
+
+ if (velocity_points)
+ {
+ return slope + intercept / speed;
+ }
+ else
+ {
+ return slope * speed + intercept;
+ }
+ }
+
+ void fill(const vec2<float>* points, int length)
+ {
+ first_point_speed = points[0].x;
+ last_arbitrary_index = length - 1;
+ // -2 because the last index in the arbitrary array is used for slope-intercept only
+ last_point_speed = points[length-2].x;
+
+ int start = static_cast<int>(floor(log(first_point_speed)));
+ first_log_lookup_speed = exp(start*1.0);
+ int end = static_cast<int>(floor(log(last_point_speed)));
+ last_log_lookup_speed = exp(end*1.0);
+ int num = end > start ? static_cast<int>(capacity / (end - start)) : 1;
+ range = fp_rep_range{ start, end, num };
+ last_log_lookup_index = end > start ? num * (end - start) - 1 : 0;
+
+ vec2<float> current = {0, velocity_points ? 0.0f : 1.0f };
+ vec2<float> next;
+ int log_index = 0;
+ double log_inner_iterator = range.start;
+ double log_inner_slice = 1.0 / (range.num * 1.0);
+ double log_value = exp(log_inner_iterator);
+
+ for (int i = 0; i < length; i++)
+ {
+ next = points[i];
+ double slope = (next.y - current.y) / (next.x - current.x);
+ double intercept = next.y - slope * next.x;
+ si_pair current_si = {
+ static_cast<float>(slope),
+ static_cast<float>(intercept)
+ };
+ arbitrary_lut_point current_lut_point = {
+ static_cast<float>(current.x),
+ current_si
+ };
+
+ this->data[i] = current_lut_point;
+
+ while (log_value < next.x && log_inner_iterator < end)
+ {
+ this->log_lookup[log_index] = i;
+ log_index++;
+ log_inner_iterator += log_inner_slice;
+ log_value = exp(log_inner_iterator);
+ }
+
+ current = next;
+ }
+ }
+
+ arbitrary_lut(const accel_args& args)
+ {
+ velocity_points = args.arb_args.velocity;
+ fill(args.arb_args.data, args.arb_args.length);
+ }
+ };
+}
diff --git a/common/accel-motivity.hpp b/common/accel-motivity.hpp
index 246cf37..0efe7ea 100644
--- a/common/accel-motivity.hpp
+++ b/common/accel-motivity.hpp
@@ -1,101 +1,56 @@
#pragma once
-#include <math.h>
-
-#include "accel-base.hpp"
+#include "accel-lookup.hpp"
-#define RA_LOOKUP
+#include <math.h>
namespace rawaccel {
- constexpr size_t LUT_SIZE = 601;
-
- struct si_pair {
- double slope = 0;
- double intercept = 0;
- };
-
- /// <summary> Struct to hold sigmoid (s-shaped) gain implementation. </summary>
- struct motivity_impl {
- double rate;
- double limit;
+ struct sigmoid {
+ double accel;
+ double motivity;
double midpoint;
- double subtractive_constant;
+ double constant;
- motivity_impl(const accel_args& args) :
- rate(pow(10,args.accel)), limit(2*log10(args.limit)), midpoint(log10(args.midpoint))
- {
- subtractive_constant = limit / 2;
- }
+ sigmoid(const accel_args& args) :
+ accel(exp(args.growth_rate)),
+ motivity(2 * log(args.motivity)),
+ midpoint(log(args.midpoint)),
+ constant(-motivity / 2) {}
- inline double operator()(double speed) const {
- double log_speed = log10(speed);
- return pow(10, limit / (exp(-rate * (log_speed - midpoint)) + 1) - subtractive_constant);
-
- }
-
- inline double legacy_offset(double speed) const { return operator()(speed); }
-
- inline double apply(si_pair* lookup, double speed) const
+ double operator()(double x) const
{
- si_pair pair = lookup[map(speed)];
- return pair.slope + pair.intercept / speed;
+ double denom = exp(accel * (midpoint - log(x))) + 1;
+ return exp(motivity / denom + constant);
}
+ };
- inline int map(double speed) const
- {
- int index = speed > 0 ? (int)(100 * log10(speed) + 201) : 0;
-
- if (index < 0) return 0;
- if (index >= LUT_SIZE) return LUT_SIZE - 1;
+ /// <summary> Struct to hold sigmoid (s-shaped) gain implementation. </summary>
+ struct motivity : binlog_lut {
- return index;
- }
+ using binlog_lut::operator();
- inline void fill(si_pair* lookup) const
+ motivity(const accel_args& args) :
+ binlog_lut(args)
{
- double lookup_speed = 0;
- double integral_interval = 0;
- double gain_integral_speed = 0;
- double gain_integral_speed_prev = 0;
- double gain = 0;
- double intercept = 0;
- double output = 0;
- double output_prev = 0;
- double x = -2;
-
- double logarithm_interval = 0.01;
- double integral_intervals_per_speed = 10;
- double integral_interval_factor = pow(10, logarithm_interval) / integral_intervals_per_speed;
-
- lookup[0] = {};
-
- for (size_t i = 1; i < LUT_SIZE; i++)
- {
- x += logarithm_interval;
-
- // Each lookup speed will be 10^0.01 = 2.33% higher than the previous
- // To get 10 integral intervals per speed, set interval to 0.233%
- lookup_speed = pow(10, x);
- integral_interval = lookup_speed * integral_interval_factor;
-
- while (gain_integral_speed < lookup_speed)
- {
- output_prev = output;
- gain_integral_speed_prev = gain_integral_speed;
- gain_integral_speed += integral_interval;
- gain = operator()(gain_integral_speed);
- output += gain * integral_interval;
+ double sum = 0;
+ double a = 0;
+ auto sigmoid_sum = [&, sig = sigmoid(args)](double b) mutable {
+ double interval = (b - a) / args.spaced_args.partitions;
+ for (int i = 1; i <= args.spaced_args.partitions; i++) {
+ sum += sig(a + i * interval) * interval;
}
-
- intercept = output_prev - gain_integral_speed_prev * gain;
-
- lookup[i] = { gain, intercept };
- }
-
+ a = b;
+ return sum;
+ };
+
+ fill([&](double x) {
+ double y = sigmoid_sum(x);
+ if (!this->transfer) y /= x;
+ return y;
+ });
}
- };
- using accel_motivity = nonadditive_accel<motivity_impl>;
+ };
}
diff --git a/common/accel-natural.hpp b/common/accel-natural.hpp
index 03700c1..9f76d1a 100644
--- a/common/accel-natural.hpp
+++ b/common/accel-natural.hpp
@@ -1,35 +1,55 @@
#pragma once
-#include <math.h>
+#include "rawaccel-base.hpp"
-#include "accel-base.hpp"
+#include <math.h>
namespace rawaccel {
/// <summary> Struct to hold "natural" (vanishing difference) acceleration implementation. </summary>
- struct natural_impl {
- double rate;
- double limit;
+ struct natural_base {
double offset;
+ double accel;
+ double limit;
- natural_impl(const accel_args& args) :
- rate(args.accel), limit(args.limit - 1), offset(args.offset)
+ natural_base(const accel_args& args) :
+ offset(args.offset),
+ limit(args.limit - 1)
{
- rate /= limit;
+ accel = args.decay_rate / fabs(limit);
}
+ };
- inline double operator()(double speed) const {
- // f(x) = k(1-e^(-mx))
- double base_speed = speed + offset;
- return limit * (1 - ((exp(-rate * speed) * speed + offset) / base_speed));
- }
+ struct natural_legacy : natural_base {
+
+ double operator()(double x) const
+ {
+ if (x <= offset) return 1;
- inline double legacy_offset(double speed) const {
- return limit - (limit * exp(-rate * speed));
+ double offset_x = offset - x;
+ double decay = exp(accel * offset_x);
+ return limit * (1 - (offset - decay * offset_x) / x) + 1;
}
+ using natural_base::natural_base;
};
- using accel_natural = additive_accel<natural_impl>;
+ struct natural : natural_base {
+ double constant;
+
+ double operator()(double x) const
+ {
+ if (x <= offset) return 1;
+
+ double offset_x = offset - x;
+ double decay = exp(accel * offset_x);
+ double output = limit * (decay / accel - offset_x) + constant;
+ return output / x + 1;
+ }
+
+ natural(const accel_args& args) :
+ natural_base(args),
+ constant(-limit / accel) {}
+ };
}
diff --git a/common/accel-naturalgain.hpp b/common/accel-naturalgain.hpp
deleted file mode 100644
index cdfd1fa..0000000
--- a/common/accel-naturalgain.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <math.h>
-
-#include "accel-natural.hpp"
-
-namespace rawaccel {
-
- /// <summary> Struct to hold "natural" (vanishing difference) gain implementation. </summary>
- struct naturalgain_impl : natural_impl {
-
- using natural_impl::natural_impl;
-
- inline double operator()(double speed) const {
- // f(x) = k((e^(-mx)-1)/mx + 1)
- double base_speed = speed + offset;
- double scaled_speed = rate * speed;
- return limit * (((exp(-scaled_speed) - 1) / (base_speed * rate) ) + 1 - offset / base_speed);
- }
-
- inline double legacy_offset(double speed) const {
- double scaled_speed = rate * speed;
- return limit * (((exp(-scaled_speed) - 1) / scaled_speed) + 1);
- }
- };
-
- using accel_naturalgain = additive_accel<naturalgain_impl>;
-}
diff --git a/common/accel-noaccel.hpp b/common/accel-noaccel.hpp
index c803c2f..8d1e758 100644
--- a/common/accel-noaccel.hpp
+++ b/common/accel-noaccel.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "accel-base.hpp"
+#include "rawaccel-base.hpp"
namespace rawaccel {
@@ -10,9 +10,7 @@ namespace rawaccel {
accel_noaccel(const accel_args&) {}
accel_noaccel() = default;
- inline double operator()(double) const { return 1; }
-
- inline double legacy_offset(double speed) const { return operator()(speed); }
+ double operator()(double) const { return 1; }
};
}
diff --git a/common/accel-power.hpp b/common/accel-power.hpp
index 5d0c451..c8faabb 100644
--- a/common/accel-power.hpp
+++ b/common/accel-power.hpp
@@ -1,26 +1,27 @@
#pragma once
-#include <math.h>
+#include "rawaccel-base.hpp"
-#include "accel-base.hpp"
+#include <math.h>
namespace rawaccel {
/// <summary> Struct to hold power (non-additive) acceleration implementation. </summary>
- struct power_impl {
- double scale;
+ struct power {
+ double pre_scale;
double exponent;
+ double post_scale;
- power_impl(const accel_args& args) :
- scale(args.scale), exponent(args.exponent)
- {}
+ power(const accel_args& args) :
+ pre_scale(args.scale),
+ exponent(args.exponent),
+ post_scale(args.weight) {}
- inline double operator()(double speed) const {
+ double operator()(double speed) const
+ {
// f(x) = (mx)^k
- return pow(speed * scale, exponent);
+ return post_scale * pow(speed * pre_scale, exponent);
}
};
- using accel_power = nonadditive_accel<power_impl>;
-
}
diff --git a/common/accel-union.hpp b/common/accel-union.hpp
new file mode 100644
index 0000000..8495a62
--- /dev/null
+++ b/common/accel-union.hpp
@@ -0,0 +1,102 @@
+#pragma once
+
+#include "accel-classic.hpp"
+#include "accel-jump.hpp"
+#include "accel-natural.hpp"
+#include "accel-power.hpp"
+#include "accel-motivity.hpp"
+#include "accel-noaccel.hpp"
+
+namespace rawaccel {
+
+ enum class internal_mode {
+ classic_lgcy,
+ classic_gain,
+ jump_lgcy,
+ jump_gain,
+ natural_lgcy,
+ natural_gain,
+ motivity_lgcy,
+ motivity_gain,
+ power,
+ lut_arb,
+ lut_log,
+ lut_lin,
+ noaccel
+ };
+
+ constexpr internal_mode make_mode(accel_mode mode, spaced_lut_mode lut_mode, bool legacy)
+ {
+ if (lut_mode != spaced_lut_mode::off) {
+ switch (lut_mode) {
+ case spaced_lut_mode::binlog: return internal_mode::lut_log;
+ case spaced_lut_mode::linear: return internal_mode::lut_lin;
+ default: return internal_mode::noaccel;
+ }
+ }
+ else if (mode == accel_mode::power) {
+ return internal_mode::power;
+ }
+ else if (mode == accel_mode::arb_lookup) {
+ return internal_mode::lut_arb;
+ }
+ else if (mode >= accel_mode::noaccel) {
+ return internal_mode::noaccel;
+ }
+ else {
+ int im = static_cast<int>(mode) * 2 + (legacy ? 0 : 1);
+ return static_cast<internal_mode>(im);
+ }
+ }
+
+ constexpr internal_mode make_mode(const accel_args& args)
+ {
+ return make_mode(args.mode, args.spaced_args.mode, args.legacy);
+ }
+
+ template <typename Visitor, typename AccelUnion>
+ constexpr auto visit_accel(Visitor vis, internal_mode mode, AccelUnion&& u)
+ {
+ switch (mode) {
+ case internal_mode::classic_lgcy: return vis(u.classic_l);
+ case internal_mode::classic_gain: return vis(u.classic_g);
+ case internal_mode::jump_lgcy: return vis(u.jump_l);
+ case internal_mode::jump_gain: return vis(u.jump_g);
+ case internal_mode::natural_lgcy: return vis(u.natural_l);
+ case internal_mode::natural_gain: return vis(u.natural_g);
+ case internal_mode::motivity_lgcy: return vis(u.motivity_l);
+ case internal_mode::motivity_gain: return vis(u.motivity_g);
+ case internal_mode::power: return vis(u.power);
+ case internal_mode::lut_arb: return vis(u.arb_lut);
+ case internal_mode::lut_log: return vis(u.log_lut);
+ case internal_mode::lut_lin: return vis(u.lin_lut);
+ default: return vis(u.noaccel);
+ }
+ }
+
+ union accel_union {
+ classic classic_g;
+ classic_legacy classic_l;
+ jump jump_g;
+ jump_legacy jump_l;
+ natural natural_g;
+ natural_legacy natural_l;
+ power power;
+ sigmoid motivity_l;
+ motivity motivity_g;
+ linear_lut lin_lut;
+ binlog_lut log_lut;
+ arbitrary_lut arb_lut;
+ accel_noaccel noaccel = {};
+
+ accel_union(const accel_args& args)
+ {
+ visit_accel([&](auto& impl) {
+ impl = { args };
+ }, make_mode(args), *this);
+ }
+
+ accel_union() = default;
+ };
+
+}
diff --git a/common/common.vcxitems b/common/common.vcxitems
index ba9bd98..2cf2df2 100644
--- a/common/common.vcxitems
+++ b/common/common.vcxitems
@@ -14,23 +14,25 @@
<ProjectCapability Include="SourceItemsFromImports" />
</ItemGroup>
<ItemGroup>
- <ClInclude Include="$(MSBuildThisFileDirectory)accel-base.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-classic.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-invoke.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-jump.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-lookup.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-motivity.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)accel-linear.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-natural.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)accel-naturalgain.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-noaccel.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-power.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-union.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-error.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-io-def.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-io.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-settings.h" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-base.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-validate.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-version.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)utility-install.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)utility-rawinput.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)x64-util.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)vec2.h" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)utility.hpp" />
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/common/rawaccel-base.hpp b/common/rawaccel-base.hpp
new file mode 100644
index 0000000..c1b2db3
--- /dev/null
+++ b/common/rawaccel-base.hpp
@@ -0,0 +1,109 @@
+#pragma once
+
+#include "vec2.h"
+
+namespace rawaccel {
+ using milliseconds = double;
+
+ inline constexpr int POLL_RATE_MIN = 125;
+ inline constexpr int POLL_RATE_MAX = 8000;
+
+ inline constexpr milliseconds DEFAULT_TIME_MIN = 1000.0 / POLL_RATE_MAX / 2;
+ inline constexpr milliseconds DEFAULT_TIME_MAX = 1000.0 / POLL_RATE_MIN * 2;
+
+ inline constexpr milliseconds WRITE_DELAY = 1000;
+
+ inline constexpr size_t MAX_DEV_ID_LEN = 200;
+
+ inline constexpr size_t SPACED_LUT_CAPACITY = 1025;
+ inline constexpr size_t ARB_LUT_CAPACITY = SPACED_LUT_CAPACITY / 4;
+
+ inline constexpr double MAX_NORM = 16;
+ inline constexpr double PI = 3.14159265358979323846;
+
+ enum class accel_mode {
+ classic,
+ jump,
+ natural,
+ motivity,
+ power,
+ arb_lookup,
+ noaccel
+ };
+
+ enum class spaced_lut_mode {
+ off,
+ binlog,
+ linear
+ };
+
+ struct spaced_lut_args {
+ spaced_lut_mode mode = spaced_lut_mode::off;
+ bool transfer = true;
+ unsigned char partitions = 2;
+ short num_elements = 8;
+ double start = 0;
+ double stop = 8;
+ };
+
+ struct table_args {
+ bool velocity = true;
+ int length = 0;
+ vec2<float> data[ARB_LUT_CAPACITY] = {};
+ };
+
+ struct accel_args {
+ accel_mode mode = accel_mode::noaccel;
+ bool legacy = false;
+
+ double offset = 0;
+ double cap = 1.5;
+ double accel_classic = 0.005;
+ double decay_rate = 0.1;
+ double growth_rate = 1;
+ double motivity = 1.5;
+ double power = 2;
+ double scale = 1;
+ double weight = 1;
+ double exponent = 0.05;
+ double limit = 1.5;
+ double midpoint = 5;
+ double smooth = 0.5;
+
+ spaced_lut_args spaced_args;
+ table_args arb_args;
+ };
+
+ struct domain_args {
+ vec2d domain_weights = { 1, 1 };
+ double lp_norm = 2;
+ };
+
+ struct settings {
+ double degrees_rotation = 0;
+ double degrees_snap = 0;
+ bool combine_mags = true;
+ double dpi = 1000;
+ double speed_min = 0;
+ double speed_max = 0;
+
+ vec2<accel_args> argsv;
+ vec2d sens = { 1, 1 };
+ vec2d dir_multipliers = { 1, 1 };
+ domain_args dom_args = {};
+ vec2d range_weights = { 1, 1 };
+
+ milliseconds time_min = DEFAULT_TIME_MIN;
+ milliseconds time_max = DEFAULT_TIME_MAX;
+
+ bool ignore = false;
+ wchar_t device_id[MAX_DEV_ID_LEN] = {};
+ };
+
+ template <typename AccelFunc>
+ inline double apply_weighted(AccelFunc&& f, double x, double w)
+ {
+ return 1 + (f(x) - 1) * w;
+ }
+
+}
diff --git a/common/rawaccel-error.hpp b/common/rawaccel-error.hpp
index cdbe1e5..a9cb7b8 100644
--- a/common/rawaccel-error.hpp
+++ b/common/rawaccel-error.hpp
@@ -1,11 +1,12 @@
#pragma once
-#include <stdexcept>
+#include <system_error>
+#include <string>
namespace rawaccel {
- class error : public std::runtime_error {
- using std::runtime_error::runtime_error;
+ class error : public std::runtime_error {
+ using std::runtime_error::runtime_error;
};
class io_error : public error {
@@ -14,7 +15,25 @@ namespace rawaccel {
class install_error : public io_error {
public:
- install_error() : io_error("Raw Accel driver is not installed, run installer.exe") {}
+ install_error() :
+ io_error("Raw Accel is not installed, run installer.exe") {}
+ };
+
+ class sys_error : public io_error {
+ public:
+ sys_error(const char* msg, DWORD code = GetLastError()) :
+ io_error(build_msg(code, msg)) {}
+
+ static std::string build_msg(DWORD code, const char* msg)
+ {
+ std::string ret =
+ std::system_error(code, std::system_category(), msg).what();
+ ret += " (";
+ ret += std::to_string(code);
+ ret += ")";
+ return ret;
+ }
+
};
}
diff --git a/common/rawaccel-io-def.h b/common/rawaccel-io-def.h
index e169390..399e0f2 100644
--- a/common/rawaccel-io-def.h
+++ b/common/rawaccel-io-def.h
@@ -1,9 +1,11 @@
#pragma once
+#define NOMINMAX
+
#ifdef _KERNEL_MODE
#include <ntddk.h>
#else
-#include <winioctl.h>
+#include <Windows.h>
#endif
#define RA_DEV_TYPE 0x8888u
diff --git a/common/rawaccel-io.hpp b/common/rawaccel-io.hpp
index 703ea92..a80e254 100644
--- a/common/rawaccel-io.hpp
+++ b/common/rawaccel-io.hpp
@@ -1,66 +1,84 @@
#pragma once
-#include <system_error>
-
-#define NOMINMAX
-#include <Windows.h>
-
#include "rawaccel-io-def.h"
-#include "rawaccel-settings.h"
#include "rawaccel-version.h"
#include "rawaccel-error.hpp"
+#include "rawaccel.hpp"
#pragma warning(push)
#pragma warning(disable:4245) // int -> DWORD conversion while passing CTL_CODE
namespace rawaccel {
- void io_control(DWORD code, void* in, DWORD in_size, void* out, DWORD out_size) {
- HANDLE ra_handle = INVALID_HANDLE_VALUE;
-
- ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0);
-
- if (ra_handle == INVALID_HANDLE_VALUE) {
- throw install_error();
- }
-
- DWORD dummy;
-
- BOOL success = DeviceIoControl(
- ra_handle,
- code,
- in,
- in_size,
- out,
- out_size,
- &dummy, // bytes returned
- NULL // overlapped structure
- );
-
- CloseHandle(ra_handle);
-
- if (!success) {
- throw std::system_error(GetLastError(), std::system_category(), "DeviceIoControl failed");
- }
- }
-
- settings read() {
- settings args;
- io_control(RA_READ, NULL, 0, &args, sizeof(settings));
- return args;
- }
-
-
- void write(const settings& args) {
- auto in_ptr = const_cast<settings*>(&args);
- io_control(RA_WRITE, in_ptr, sizeof(settings), NULL, 0);
- }
-
- version_t get_version() {
- version_t ver;
- io_control(RA_GET_VERSION, NULL, 0, &ver, sizeof(version_t));
- return ver;
- }
+ inline void io_control(DWORD code, void* in, DWORD in_size, void* out, DWORD out_size)
+ {
+ HANDLE ra_handle = INVALID_HANDLE_VALUE;
+
+ ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0);
+
+ if (ra_handle == INVALID_HANDLE_VALUE) {
+ throw install_error();
+ }
+
+ DWORD dummy;
+
+ BOOL success = DeviceIoControl(
+ ra_handle,
+ code,
+ in,
+ in_size,
+ out,
+ out_size,
+ &dummy, // bytes returned
+ NULL // overlapped structure
+ );
+
+ CloseHandle(ra_handle);
+
+ if (!success) {
+ throw sys_error("DeviceIoControl failed");
+ }
+ }
+
+ inline void read(io_t& args)
+ {
+ io_control(RA_READ, NULL, 0, &args, sizeof(io_t));
+ }
+
+ inline void write(const io_t& args)
+ {
+ io_control(RA_WRITE, const_cast<io_t*>(&args), sizeof(io_t), NULL, 0);
+ }
+
+ inline version_t get_version()
+ {
+ version_t v;
+
+ try {
+ io_control(RA_GET_VERSION, NULL, 0, &v, sizeof(version_t));
+ }
+ catch (const sys_error&) {
+ // assume request is not implemented (< 1.3)
+ v = { 0 };
+ }
+
+ return v;
+ }
+
+ inline version_t valid_version_or_throw()
+ {
+ auto v = get_version();
+
+ if (v < min_driver_version) {
+ throw error("reinstallation required");
+ }
+
+ if (version < v) {
+ throw error("newer driver is installed");
+ }
+
+ return v;
+ }
}
diff --git a/common/rawaccel-settings.h b/common/rawaccel-settings.h
deleted file mode 100644
index dedef6d..0000000
--- a/common/rawaccel-settings.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include "vec2.h"
-#include "accel-base.hpp"
-
-#define MAX_DEV_ID_LEN 200
-
-namespace rawaccel {
-
- using milliseconds = double;
-
- inline constexpr int MAX_POLL_RATE_KHZ = 8;
- inline constexpr milliseconds DEFAULT_TIME_MIN = 1.0 / MAX_POLL_RATE_KHZ * 0.8;
- inline constexpr milliseconds WRITE_DELAY = 1000;
-
- enum class accel_mode {
- linear, classic, natural, naturalgain, power, motivity, noaccel
- };
-
- struct settings {
- double degrees_rotation = 0;
- double degrees_snap = 0;
- bool combine_mags = true;
- vec2<accel_mode> modes = { accel_mode::noaccel, accel_mode::noaccel };
- vec2<accel_args> argsv;
- vec2d sens = { 1, 1 };
- vec2d dir_multipliers = {};
- domain_args domain_args = {};
- vec2d range_weights = { 1, 1 };
- milliseconds time_min = DEFAULT_TIME_MIN;
- wchar_t device_id[MAX_DEV_ID_LEN] = {0};
- };
-
-}
diff --git a/common/rawaccel-validate.hpp b/common/rawaccel-validate.hpp
new file mode 100644
index 0000000..e9d1120
--- /dev/null
+++ b/common/rawaccel-validate.hpp
@@ -0,0 +1,185 @@
+#pragma once
+
+#include "rawaccel-base.hpp"
+#include "utility.hpp"
+
+namespace rawaccel {
+
+ struct valid_ret_t {
+ int count_x = 0;
+ int count_y = 0;
+ int count = 0;
+
+ explicit operator bool() const
+ {
+ return count == 0;
+ }
+ };
+
+ template <typename MsgHandler = noop>
+ valid_ret_t valid(const settings& args, MsgHandler fn = {})
+ {
+ valid_ret_t ret;
+
+ auto error = [&](auto msg) {
+ ++ret.count;
+ fn(msg);
+ };
+
+ auto check_accel = [&error](const accel_args& args) {
+ static_assert(SPACED_LUT_CAPACITY == 1025, "update error msg");
+
+ const auto& lut_args = args.spaced_args;
+
+ if (lut_args.partitions <= 0) {
+ error("lut partitions"" must be positive");
+ }
+
+ if (lut_args.mode == spaced_lut_mode::linear) {
+ if (lut_args.start <= 0) {
+ error("start"" must be positive");
+ }
+
+ if (lut_args.stop <= lut_args.start) {
+ error("stop must be greater than start");
+ }
+
+ if (lut_args.num_elements < 2 ||
+ lut_args.num_elements > 1025) {
+ error("num must be between 2 and 1025");
+ }
+ }
+ else if (lut_args.mode == spaced_lut_mode::binlog) {
+ int istart = static_cast<int>(lut_args.start);
+ int istop = static_cast<int>(lut_args.stop);
+
+ if (lut_args.start < -99) {
+ error("start is too small");
+ }
+ else if (lut_args.stop > 99) {
+ error("stop is too large");
+ }
+ else if (istart != lut_args.start || istop != lut_args.stop) {
+ error("start and stop must be integers");
+ }
+ else if (istop <= istart) {
+ error("stop must be greater than start");
+ }
+ else if (lut_args.num_elements <= 0) {
+ error("num"" must be positive");
+ }
+ else if (((lut_args.stop - lut_args.start) * lut_args.num_elements) >= 1025) {
+ error("binlog mode requires (num * (stop - start)) < 1025");
+ }
+ }
+
+ if (args.mode == accel_mode::arb_lookup) {
+ if (args.arb_args.length < 2) {
+ error("lookup mode requires at least 2 points");
+ }
+ }
+
+ if (args.offset < 0) {
+ error("offset can not be negative");
+ }
+
+ if (args.cap < 0) {
+ error("cap"" must not be negative");
+ }
+
+ if (args.growth_rate <= 0 ||
+ args.decay_rate <= 0 ||
+ args.accel_classic <= 0) {
+ error("acceleration"" must be positive");
+ }
+
+ if (args.motivity <= 1) {
+ error("motivity must be greater than 1");
+ }
+
+ if (args.power <= 1) {
+ error("power must be greater than 1");
+ }
+
+ if (args.scale <= 0) {
+ error("scale"" must be positive");
+ }
+
+ if (args.weight <= 0) {
+ error("weight"" must be positive");
+ }
+
+ if (args.exponent <= 0) {
+ error("exponent"" must be positive");
+ }
+
+ if (args.limit <= 0) {
+ error("limit"" must be positive");
+ }
+
+ if (args.midpoint <= 0) {
+ error("midpoint"" must be positive");
+ }
+
+ if (args.smooth < 0 || args.smooth > 1) {
+ error("smooth must be between 0 and 1");
+ }
+
+ };
+
+ check_accel(args.argsv.x);
+
+ if (!args.combine_mags) {
+ ret.count_x = ret.count;
+ check_accel(args.argsv.y);
+ ret.count_y = ret.count;
+ }
+
+ if (args.dpi <= 0) {
+ error("dpi"" must be positive");
+ }
+
+ if (args.speed_max < 0) {
+ error("speed cap is negative");
+ }
+ else if (args.speed_max < args.speed_min) {
+ error("max speed is less than min speed");
+ }
+
+ if (args.degrees_snap < 0 || args.degrees_snap > 45) {
+ error("snap angle must be between 0 and 45 degrees");
+ }
+
+ if (args.sens.x == 0 || args.sens.y == 0) {
+ error("sens multiplier is 0");
+ }
+
+ if (args.dom_args.domain_weights.x <= 0 ||
+ args.dom_args.domain_weights.y <= 0) {
+ error("domain weights"" must be positive");
+ }
+
+ if (args.dir_multipliers.x <= 0 || args.dir_multipliers.y <= 0) {
+ error("directional multipliers must be positive");
+ }
+
+ if (args.dom_args.lp_norm < 2) {
+ error("Lp norm is less than 2 (default=2)");
+ }
+
+ if (args.range_weights.x <= 0 || args.range_weights.y <= 0) {
+ error("range weights"" must be positive");
+ }
+
+ if (args.time_min <= 0) {
+ error("minimum time"" must be positive");
+ }
+
+ if (args.time_max < args.time_min) {
+ error("max time is less than min time");
+ }
+
+ return ret;
+ }
+
+}
diff --git a/common/rawaccel-version.h b/common/rawaccel-version.h
index ce53b30..384ba6f 100644
--- a/common/rawaccel-version.h
+++ b/common/rawaccel-version.h
@@ -1,26 +1,36 @@
#pragma once
#define RA_VER_MAJOR 1
-#define RA_VER_MINOR 4
-#define RA_VER_PATCH 4
+#define RA_VER_MINOR 5
+#define RA_VER_PATCH 0
#define RA_OS "Win7+"
-#define M_STR_HELPER(x) #x
-#define M_STR(x) M_STR_HELPER(x)
+#define RA_M_STR_HELPER(x) #x
+#define RA_M_STR(x) RA_M_STR_HELPER(x)
-#define RA_VER_STRING M_STR(RA_VER_MAJOR) "." M_STR(RA_VER_MINOR) "." M_STR(RA_VER_PATCH)
+#define RA_VER_STRING RA_M_STR(RA_VER_MAJOR) "." RA_M_STR(RA_VER_MINOR) "." RA_M_STR(RA_VER_PATCH)
namespace rawaccel {
- struct version_t {
- int major;
- int minor;
- int patch;
- };
-
+ struct version_t {
+ int major;
+ int minor;
+ int patch;
+ };
+
+ constexpr bool operator<(const version_t& lhs, const version_t& rhs)
+ {
+ return (lhs.major != rhs.major) ?
+ (lhs.major < rhs.major) :
+ (lhs.minor != rhs.minor) ?
+ (lhs.minor < rhs.minor) :
+ (lhs.patch < rhs.patch) ;
+ }
+
+ inline constexpr version_t version = { RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH };
#ifndef _KERNEL_MODE
- inline constexpr version_t min_driver_version = { 1, 4, 0 };
+ inline constexpr version_t min_driver_version = { 1, 5, 0 };
#endif
}
diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp
index e28cd92..fb2d81a 100644
--- a/common/rawaccel.hpp
+++ b/common/rawaccel.hpp
@@ -1,419 +1,179 @@
#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"
+#include "accel-invoke.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;
- };
-
- struct snapper {
- double threshold = 0;
-
- inline vec2d apply(const vec2d& input) const {
- if (input.x != 0 && input.y != 0) {
- double angle = fabs(atan(input.y / input.x));
- auto mag = [&] { return sqrtsd(input.x * input.x + input.y * input.y); };
-
- if (angle > M_PI_2 - threshold) return { 0, _copysign(mag(), input.y) };
- if (angle < threshold) return { _copysign(mag(), input.x), 0 };
- }
-
- return input;
- }
-
- snapper(double degrees) : threshold(minsd(fabs(degrees), 45) * M_PI / 180) {}
-
- snapper() = default;
- };
-
- /// <summary> Struct to hold clamp (min and max) details for acceleration application </summary>
- struct accel_scale_clamp {
- double lo = 0;
- double hi = 1e9;
-
- /// <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);
- }
+ inline vec2d direction(double degrees)
+ {
+ double radians = degrees * PI / 180;
+ return { cos(radians), sin(radians) };
+ }
- accel_scale_clamp(double cap) {
- if (cap <= 0) {
- // use default, effectively uncapped accel
- return;
- }
+ constexpr vec2d rotate(const vec2d& v, const vec2d& direction)
+ {
+ return {
+ v.x * direction.x - v.y * direction.y,
+ v.x * direction.y + v.y * direction.x
+ };
+ }
- if (cap < 1) {
- // assume negative accel
- lo = cap;
- hi = 1;
- }
- else hi = cap;
- }
+ inline double magnitude(const vec2d& v)
+ {
+ return sqrt(v.x * v.x + v.y * v.y);
+ }
- 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);
- }
+ inline double lp_distance(const vec2d& v, double p)
+ {
+ return pow(pow(v.x, p) + pow(v.y, p), 1 / p);
}
- struct accel_variant {
- si_pair* lookup;
+ class mouse_modifier {
+ public:
+ enum accel_distance_mode : unsigned char {
+ separate,
+ max,
+ Lp,
+ euclidean,
+ };
- accel_mode tag = accel_mode::noaccel;
+ bool apply_rotate = false;
+ bool compute_ref_angle = false;
+ bool apply_snap = false;
+ bool cap_speed = false;
+ accel_distance_mode dist_mode = euclidean;
+ bool apply_directional_weight = false;
+ bool apply_dir_mul_x = false;
+ bool apply_dir_mul_y = false;
- 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 = {};
+ vec2d rot_vec = { 1, 0 };
+ double snap = 0;
+ double dpi_norm_factor = 1;
+ double speed_min = 0;
+ double speed_max = 0;
+ vec2d domain_weights = { 1, 1 };
+ double p = 2;
+ vec2d range_weights = { 1, 1 };
+ vec2d directional_multipliers = { 1, 1 };
+ vec2d sensitivity = { 1, 1 };
+ vec2<accel_union> accel;
- accel_variant(const accel_args& args, accel_mode mode, si_pair* lut = nullptr) :
- tag(mode), lookup(lut)
+#ifdef _KERNEL_MODE
+ __forceinline
+#endif
+ void modify(vec2d& in, const vec2<accel_invoker>& inv, milliseconds time = 1) const
{
- visit_accel([&](auto& impl) {
- impl = { args };
- }, *this);
+ double ips_factor = dpi_norm_factor / time;
+ double reference_angle = 0;
- if (lookup && tag == accel_mode::motivity) {
- u.motivity.fn.fill(lookup);
- }
+ if (apply_rotate) in = rotate(in, rot_vec);
- }
-
- inline double apply(double speed) const {
- if (lookup && tag == accel_mode::motivity) {
- return u.motivity.fn.apply(lookup, speed);
+ if (compute_ref_angle && in.y != 0) {
+ if (in.x == 0) {
+ reference_angle = PI / 2;
+ }
+ else {
+ reference_angle = atan(fabs(in.y / in.x));
+
+ if (apply_snap) {
+ if (reference_angle > PI / 2 - snap) {
+ reference_angle = PI / 2;
+ in = { 0, _copysign(magnitude(in), in.y) };
+ }
+ else if (reference_angle < snap) {
+ reference_angle = 0;
+ in = { _copysign(magnitude(in), in.x), 0 };
+ }
+ }
+ }
}
- 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);
+ if (cap_speed) {
+ double speed = magnitude(in) * ips_factor;
+ double ratio = clampsd(speed, speed_min, speed_max) / speed;
+ in.x *= ratio;
+ in.y *= ratio;
}
- scale = clamp(scale);
+ vec2d abs_weighted_vel = {
+ fabs(in.x * ips_factor * domain_weights.x),
+ fabs(in.y * ips_factor * domain_weights.y)
+ };
- if (output_speed_cap > 0 && (scale * speed) > output_speed_cap) {
- scale = output_speed_cap / speed;
+ if (dist_mode == separate) {
+ in.x *= inv.x.invoke(accel.x, abs_weighted_vel.x, range_weights.x);
+ in.y *= inv.y.invoke(accel.y, abs_weighted_vel.y, range_weights.y);
}
+ else {
+ double speed;
- return scale;
- }
+ if (dist_mode == max) {
+ speed = maxsd(abs_weighted_vel.x, abs_weighted_vel.y);
+ }
+ else if (dist_mode == Lp) {
+ speed = lp_distance(abs_weighted_vel, p);
+ }
+ else {
+ speed = magnitude(abs_weighted_vel);
+ }
- accelerator() = default;
- };
+ double weight = range_weights.x;
- struct weighted_distance {
- double p = 2.0;
- double p_inverse = 0.5;
- bool lp_norm_infinity = false;
- double sigma_x = 1.0;
- double sigma_y = 1.0;
+ if (apply_directional_weight) {
+ double diff = range_weights.y - range_weights.x;
+ weight += 2 / PI * reference_angle * diff;
+ }
- weighted_distance(const domain_args& args)
- {
- sigma_x = args.domain_weights.x;
- sigma_y = args.domain_weights.y;
- if (args.lp_norm <= 0)
- {
- lp_norm_infinity = true;
- p = 0.0;
- p_inverse = 0.0;
- }
- else
- {
- lp_norm_infinity = false;
- p = args.lp_norm;
- p_inverse = 1 / args.lp_norm;
+ double scale = inv.x.invoke(accel.x, speed, weight);
+ in.x *= scale;
+ in.y *= scale;
}
- }
-
- inline double calculate(double x, double y)
- {
- double abs_x = fabs(x);
- double abs_y = fabs(y);
-
- if (lp_norm_infinity) return maxsd(abs_x, abs_y);
-
- double x_scaled = abs_x * sigma_x;
- double y_scaled = abs_y * sigma_y;
-
- if (p == 2) return sqrtsd(x_scaled * x_scaled + y_scaled * y_scaled);
- else return pow(pow(x_scaled, p) + pow(y_scaled, p), p_inverse);
- }
-
- weighted_distance() = default;
- };
-
- struct direction_weight {
- double diff = 0.0;
- double start = 1.0;
- bool should_apply = false;
-
- direction_weight(const vec2d& thetas)
- {
- diff = thetas.y - thetas.x;
- start = thetas.x;
-
- should_apply = diff != 0;
- }
-
- inline double atan_scale(double x, double y)
- {
- return M_2_PI * atan2(fabs(y), fabs(x));
- }
-
- inline double apply(double x, double y)
- {
- return atan_scale(x, y) * diff + start;
- }
- direction_weight() = default;
- };
-
- /// <summary> Struct to hold variables and methods for modifying mouse input </summary>
- struct mouse_modifier {
- bool apply_rotate = false;
- bool apply_snap = false;
- bool apply_accel = false;
- bool combine_magnitudes = true;
- rotator rotate;
- snapper snap;
- weighted_distance distance;
- direction_weight directional;
- vec2<accelerator> accels;
- vec2d sensitivity = { 1, 1 };
- vec2d directional_multipliers = {};
-
- 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 (apply_dir_mul_x && in.x < 0) {
+ in.x *= directional_multipliers.x;
}
-
- if (args.degrees_snap != 0) {
- snap = snapper(args.degrees_snap);
- apply_snap = true;
- }
-
- if (args.sens.x != 0) sensitivity.x = args.sens.x;
- if (args.sens.y != 0) sensitivity.y = args.sens.y;
- directional_multipliers.x = fabs(args.dir_multipliers.x);
- directional_multipliers.y = fabs(args.dir_multipliers.y);
-
- if ((combine_magnitudes && args.modes.x == accel_mode::noaccel) ||
- (args.modes.x == accel_mode::noaccel &&
- args.modes.y == accel_mode::noaccel)) {
- return;
+ if (apply_dir_mul_y && in.y < 0) {
+ in.y *= directional_multipliers.y;
}
- accels.x = accelerator(args.argsv.x, args.modes.x, luts.x);
- accels.y = accelerator(args.argsv.y, args.modes.y, luts.y);
-
- distance = weighted_distance(args.domain_args);
- directional = direction_weight(args.range_weights);
-
- apply_accel = true;
- }
-
- void modify(vec2d& movement, milliseconds time) {
- apply_rotation(movement);
- apply_angle_snap(movement);
- apply_acceleration(movement, [=] { return time; });
- apply_sensitivity(movement);
- }
-
- inline void apply_rotation(vec2d& movement) {
- if (apply_rotate) movement = rotate.apply(movement);
+ in.x *= sensitivity.x;
+ in.y *= sensitivity.y;
}
- inline void apply_angle_snap(vec2d& movement) {
- if (apply_snap) movement = snap.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 = distance.calculate(movement.x, movement.y);
- double speed = mag / time;
- double scale = accels.x.apply(speed);
-
- if (directional.should_apply)
- {
- scale = (scale - 1)*directional.apply(movement.x, movement.y) + 1;
- }
-
- 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(const settings& args) :
+ rot_vec(direction(args.degrees_rotation)),
+ snap(args.degrees_snap * PI / 180),
+ dpi_norm_factor(1000 / args.dpi),
+ speed_min(args.speed_min),
+ speed_max(args.speed_max),
+ p(args.dom_args.lp_norm),
+ domain_weights(args.dom_args.domain_weights),
+ range_weights(args.range_weights),
+ directional_multipliers(args.dir_multipliers),
+ sensitivity(args.sens),
+ accel({ { args.argsv.x }, { args.argsv.y } })
+ {
+ cap_speed = speed_max > 0 && speed_min <= speed_max;
+ apply_rotate = rot_vec.x != 1;
+ apply_snap = snap != 0;
+ apply_directional_weight = range_weights.x != range_weights.y;
+ compute_ref_angle = apply_snap || apply_directional_weight;
+ apply_dir_mul_x = directional_multipliers.x != 1;
+ apply_dir_mul_y = directional_multipliers.y != 1;
- if (directional_multipliers.x > 0 && movement.x < 0) {
- movement.x *= directional_multipliers.x;
- }
- if (directional_multipliers.y > 0 && movement.y < 0) {
- movement.y *= directional_multipliers.y;
- }
+ if (!args.combine_mags) dist_mode = separate;
+ else if (p >= MAX_NORM) dist_mode = max;
+ else if (p > 2) dist_mode = Lp;
+ else dist_mode = euclidean;
}
mouse_modifier() = default;
};
+ struct io_t {
+ settings args;
+ mouse_modifier mod;
+ };
+
} // rawaccel
diff --git a/common/utility-rawinput.hpp b/common/utility-rawinput.hpp
index c3a4576..f04b2c1 100644
--- a/common/utility-rawinput.hpp
+++ b/common/utility-rawinput.hpp
@@ -66,7 +66,7 @@ void rawinput_foreach_dev_with_id(Func fn, bool with_instance_id = false,
if (!with_instance_id) {
auto instance_delim_pos = id.find_last_of('\\');
- if(instance_delim_pos != std::string::npos) id.resize(instance_delim_pos);
+ if (instance_delim_pos != std::string::npos) id.resize(instance_delim_pos);
}
fn(dev, id);
diff --git a/common/utility.hpp b/common/utility.hpp
new file mode 100644
index 0000000..cbd19e3
--- /dev/null
+++ b/common/utility.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+namespace rawaccel {
+
+ constexpr double minsd(double a, double b)
+ {
+ return (a < b) ? a : b;
+ }
+
+ constexpr double maxsd(double a, double b)
+ {
+ return (b < a) ? a : b;
+ }
+
+ constexpr double clampsd(double v, double lo, double hi)
+ {
+ return minsd(maxsd(v, lo), hi);
+ }
+
+ template <typename T>
+ constexpr const T& min(const T& a, const T& b)
+ {
+ return (b < a) ? b : a;
+ }
+
+ template <typename T>
+ constexpr const T& max(const T& a, const T& b)
+ {
+ return (b < a) ? a : b;
+ }
+
+ template <typename T>
+ constexpr const T& clamp(const T& v, const T& lo, const T& hi)
+ {
+ return (v < lo) ? lo : (hi < v) ? hi : v;
+ }
+
+ constexpr double lerp(double a, double b, double t)
+ {
+ double x = a + t * (b - a);
+ if ((t > 1) == (a < b)) {
+ return maxsd(x, b);
+ }
+ return minsd(x, b);
+ }
+
+ // returns the unbiased exponent of x if x is normal
+ inline int ilogb(double x)
+ {
+ union { double f; unsigned long long i; } u = { x };
+ return static_cast<int>((u.i >> 52) & 0x7ff) - 0x3ff;
+ }
+
+ // returns x * 2^n if n is in [-1022, 1023]
+ inline double scalbn(double x, int n)
+ {
+ union { double f; unsigned long long i; } u;
+ u.i = static_cast<unsigned long long>(0x3ff + n) << 52;
+ return x * u.f;
+ }
+
+ inline bool infnan(double x)
+ {
+ return ilogb(x) == 0x400;
+ }
+
+ struct noop {
+ template <typename... Ts>
+ constexpr void operator()(Ts&&...) const noexcept {}
+ };
+
+ template <typename T> struct remove_ref { using type = T; };
+ template <typename T> struct remove_ref<T&> { using type = T; };
+ template <typename T> struct remove_ref<T&&> { using type = T; };
+
+ template <typename T>
+ using remove_ref_t = typename remove_ref<T>::type;
+
+ template <typename T, typename U>
+ struct is_same { static constexpr bool value = false; };
+
+ template <typename T>
+ struct is_same<T, T> { static constexpr bool value = true; };
+
+ template <typename T, typename U>
+ inline constexpr bool is_same_v = is_same<T, U>::value;
+
+}
diff --git a/common/x64-util.hpp b/common/x64-util.hpp
deleted file mode 100644
index 40bc7c4..0000000
--- a/common/x64-util.hpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#ifdef _MANAGED
-
-#include <math.h>
-inline double sqrtsd(double val) { return sqrt(val); }
-
-#else
-
-#include <emmintrin.h>
-inline double sqrtsd(double val) {
- __m128d src = _mm_load_sd(&val);
- __m128d dst = _mm_sqrt_sd(src, src);
- _mm_store_sd(&val, dst);
- return val;
-}
-
-#endif
-
-inline constexpr double minsd(double a, double b) {
- return (a < b) ? a : b;
-}
-
-inline constexpr double maxsd(double a, double b) {
- return (b < a) ? a : b;
-}
-
-inline constexpr double clampsd(double v, double lo, double hi) {
- return minsd(maxsd(v, lo), hi);
-}