Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maccel scaledowncurve+roundingcarry #6

Merged
merged 31 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
502948f
Transform to scale-down curve, and add rounding carry
Wimads Mar 5, 2024
994e950
Crediting @ankostis for rounding carry
Wimads Mar 5, 2024
597bf05
Update comments to reflect changes
Wimads Mar 6, 2024
e72651f
Resolve magic number in factor formula
Mar 7, 2024
f26ff18
Apply suggestions from code review
Mar 7, 2024
b62b53d
Document scaledown changes
Mar 7, 2024
6ac9346
Update readme.md
Wimads Mar 7, 2024
a23d431
Update desmos link and image
Wimads Mar 7, 2024
860f605
Updated desmos link
Wimads Mar 7, 2024
d0550c1
update desmos link (again)
Wimads Mar 7, 2024
cacbabd
Update desmos link in maccel.c as well
Wimads Mar 7, 2024
3168fb1
Update readme breaking changes for scaledown
Mar 8, 2024
7c74f67
Spelling corrections
Mar 8, 2024
9431c28
Readme: small math facelift
Mar 8, 2024
ef8143e
fix(maccel) forgotten KC_LIMIT=1 from upscaling
ankostis Mar 9, 2024
a4f5d93
feat(maccel) decrease limit step 0.1 -> 0.001 (upscale artifact)
ankostis Mar 9, 2024
7023cab
Added instructions to turn off OS accel; corrected limit comments in …
Wimads Mar 10, 2024
fbf76b7
Add clearer explanation of effects of LIMIT and DPI settings.
Wimads Mar 10, 2024
b89331c
Emphasize that the curve image is clickable
Wimads Mar 10, 2024
834b71c
Emphasize existence of debug console
Wimads Mar 10, 2024
c2d2f7d
Note on mac os limitations, and moving extended mouse report suggesti…
Wimads Mar 10, 2024
3a1729d
Added instructions to turn off OS accel; corrected limit comments in …
Wimads Mar 10, 2024
c1c62c7
Update readme dates
Mar 12, 2024
0bdecf1
inline math formula beautification
Mar 12, 2024
f99cbe7
spelling correction
Mar 12, 2024
ef443f6
changelog and release history correction
Mar 12, 2024
bbc0b14
change via limits to be more sensible for new curve
Mar 12, 2024
7d08ed5
wordlist expansion
Mar 12, 2024
2d761d0
reset rounding carry after timeout
Mar 12, 2024
788258e
remove mac-specific instructions since mac is unsupported
Mar 12, 2024
db9b8b4
undo inline math beautification because GH is picky
Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ ifeq
ifdef
trackpad
Desmos
rmin
rmax
tmin
tmax
Binary file modified maccel/assets/accel_curve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified maccel/assets/via.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 39 additions & 15 deletions maccel/maccel.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ static uint32_t maccel_timer;
# define MACCEL_OFFSET 2.2 // lower/higher value = acceleration kicks in earlier/later
#endif
#ifndef MACCEL_LIMIT
# define MACCEL_LIMIT 6.0 // upper limit of accel curve (maximum acceleration factor)
# define MACCEL_LIMIT 0.2 // lower limit of accel curve (minimum acceleration factor)
#endif
#ifndef MACCEL_CPI_THROTTLE_MS
# define MACCEL_CPI_THROTTLE_MS 200 // milliseconds to wait between requesting the device's current DPI
#endif
#ifndef MACCEL_LIMIT_UPPER
# define MACCEL_LIMIT_UPPER 1 // upper limit of accel curve, recommended to leave at 1; adjust DPI setting instead.
#endif
#ifndef MACCEL_ROUNDING_CARRY_TIMEOUT_MS
# define MACCEL_ROUNDING_CARRY_TIMEOUT_MS 200 // milliseconds after which to reset quantization error correction (forget rounding remainder)
Wimads marked this conversation as resolved.
Show resolved Hide resolved
#endif

maccel_config_t g_maccel_config = {
// clang-format off
Expand All @@ -45,7 +51,7 @@ maccel_config_t g_maccel_config = {
# define MACCEL_OFFSET_STEP 0.1f
# endif
# ifndef MACCEL_LIMIT_STEP
# define MACCEL_LIMIT_STEP 0.1f
# define MACCEL_LIMIT_STEP 0.01f
# endif
#endif

Expand Down Expand Up @@ -75,7 +81,7 @@ void maccel_set_offset(float val) {
g_maccel_config.offset = val;
}
void maccel_set_limit(float val) {
if (val >= 1) { // limit less than 1 leads to nonsensical results
if (val >= 0) {
g_maccel_config.limit = val;
}
}
Expand All @@ -97,14 +103,26 @@ void maccel_toggle_enabled(void) {
#define CONSTRAIN_REPORT(val) (mouse_xy_report_t) _CONSTRAIN(val, XY_REPORT_MIN, XY_REPORT_MAX)

report_mouse_t pointing_device_task_maccel(report_mouse_t mouse_report) {
// rounding carry to recycle dropped floats from int mouse reports, to smoothen low speed movements (credit @ankostis)
static float rounding_carry_x = 0;
static float rounding_carry_y = 0;
// time since last mouse report:
const uint16_t delta_time = timer_elapsed32(maccel_timer);
// skip maccel maths if report = 0, or if maccel not enabled.
if ((mouse_report.x == 0 && mouse_report.y == 0) || !g_maccel_config.enabled) {
return mouse_report;
}

// time since last mouse report:
const uint16_t delta_time = timer_elapsed32(maccel_timer);
maccel_timer = timer_read32();
// get device cpi setting, only call when mouse hasn't moved since more than 200ms
// reset timer:
maccel_timer = timer_read32();
// Reset carry if too much time passed
if (delta_time > MACCEL_ROUNDING_CARRY_TIMEOUT_MS) {
rounding_carry_x = 0;
rounding_carry_y = 0;
}
// Reset carry when pointer swaps direction, to follow user's hand.
if (mouse_report.x * rounding_carry_x < 0) rounding_carry_x = 0;
if (mouse_report.y * rounding_carry_y < 0) rounding_carry_y = 0;
// Limit expensive calls to get device cpi settings only when mouse stationary for > 200ms.
static uint16_t device_cpi = 300;
if (delta_time > MACCEL_CPI_THROTTLE_MS) {
device_cpi = pointing_device_get_cpi();
Expand All @@ -117,17 +135,23 @@ report_mouse_t pointing_device_task_maccel(report_mouse_t mouse_report) {
const float velocity_raw = distance / delta_time;
// correct raw velocity for dpi
const float velocity = dpi_correction * velocity_raw;
// calculate mouse acceleration factor: f(dv) = c - ((c-1) / ((1 + e^(x(x - b)) * a/z)))
// letter variables for readability of maths:
const float k = g_maccel_config.takeoff;
const float g = g_maccel_config.growth_rate;
const float s = g_maccel_config.offset;
const float m = g_maccel_config.limit;
// acceleration factor: y(x) = M - (M - 1) / {1 + e^[K(x - S)]}^(G/K)
// Generalised Sigmoid Function, see https://www.desmos.com/calculator/xkhejelty8
const float maccel_factor = m - (m - 1) / powf(1 + expf(k * (velocity - s)), g / k);
// calculate accelerated delta X and Y values and clamp:
const mouse_xy_report_t x = CONSTRAIN_REPORT(mouse_report.x * maccel_factor);
const mouse_xy_report_t y = CONSTRAIN_REPORT(mouse_report.y * maccel_factor);
// acceleration factor: f(v) = 1 - (1 - M) / {1 + e^[K(v - S)]}^(G/K):
// Generalised Sigmoid Function, see https://www.desmos.com/calculator/k9vr0y2gev
const float maccel_factor = MACCEL_LIMIT_UPPER - (MACCEL_LIMIT_UPPER - m) / powf(1 + expf(k * (velocity - s)), g / k);
// multiply mouse reports by acceleration factor, and account for previous quantization errors:
const float new_x = rounding_carry_x + maccel_factor * mouse_report.x;
const float new_y = rounding_carry_y + maccel_factor * mouse_report.y;
// Accumulate any difference from next integer (quantization).
rounding_carry_x = new_x - (int)new_x;
rounding_carry_y = new_y - (int)new_y;
// clamp values
const mouse_xy_report_t x = CONSTRAIN_REPORT(new_x);
const mouse_xy_report_t y = CONSTRAIN_REPORT(new_y);

// console output for debugging (enable/disable in config.h)
#ifdef MACCEL_DEBUG
Expand Down
37 changes: 25 additions & 12 deletions maccel/maccel_via.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ enum via_maccel_ids {
// clang-format on
};

#define MACCEL_VIA_TKO_MIN 0
#define MACCEL_VIA_TKO_MAX 5
#define MACCEL_VIA_GRO_MIN 0.01
#define MACCEL_VIA_GRO_MAX 5
#define MACCEL_VIA_OFS_MIN 0
#define MACCEL_VIA_OFS_MAX 15
#define MACCEL_VIA_LMT_MIN 0
#define MACCEL_VIA_LMT_MAX 1

#define MACCEL_VIA_UINT16_MIN 0
#define MACCEL_VIA_UINT16_MAX 60000 // Not using the full range for historic reasons. Should be changed with breaking change requiring via json update.

#define PROJECT(val, rmin, rmax, tmin, tmax) (((float)(val - rmin) / (float)(rmax - rmin)) * (float)(tmax - tmin)) + tmin
#define PROJECT_TO_VIA(val, rmin, rmax) PROJECT(val, rmin, rmax, MACCEL_VIA_UINT16_MIN, MACCEL_VIA_UINT16_MAX)
#define PROJECT_FROM_VIA(val, tmin, tmax) PROJECT(val, MACCEL_VIA_UINT16_MIN, MACCEL_VIA_UINT16_MAX, tmin, tmax)

#define COMBINE_UINT8(one, two) (two | (one << 8))

// Handle the data received by the keyboard from the VIA menus
Expand All @@ -36,8 +52,7 @@ void maccel_config_set_value(uint8_t *data) {
case id_maccel_takeoff: {
uint16_t takeoff = COMBINE_UINT8(value_data[0], value_data[1]);

// calc uint16 to float: takeoff moves comma and shifts by 0.5, so that 0.5..6.5 fits into 0..60k
g_maccel_config.takeoff = (takeoff / 10000.0f) + 0.5;
g_maccel_config.takeoff = PROJECT_FROM_VIA(takeoff, MACCEL_VIA_TKO_MIN, MACCEL_VIA_TKO_MAX);
#ifdef MACCEL_DEBUG
printf("MACCEL:via: TKO: %.3f grw: %.3f ofs: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit);
#endif
Expand All @@ -46,8 +61,7 @@ void maccel_config_set_value(uint8_t *data) {
case id_maccel_growth_rate: {
uint16_t growth_rate = COMBINE_UINT8(value_data[0], value_data[1]);

// calc uint16 to float: growth_rate only moves the comma
g_maccel_config.growth_rate = growth_rate / 10000.0f;
g_maccel_config.growth_rate = PROJECT_FROM_VIA(growth_rate, MACCEL_VIA_GRO_MIN, MACCEL_VIA_GRO_MAX);
#ifdef MACCEL_DEBUG
printf("MACCEL:via: tko: %.3f GRW: %.3f ofs: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit);
#endif
Expand All @@ -56,8 +70,7 @@ void maccel_config_set_value(uint8_t *data) {
case id_maccel_offset: {
uint16_t offset = COMBINE_UINT8(value_data[0], value_data[1]);

// calc uint16 to float: offset moves comma and shifts by 3, so that -3..3 fits into 0..60k
g_maccel_config.offset = (offset / 10000.0f) - 3;
g_maccel_config.offset = PROJECT_FROM_VIA(offset, MACCEL_VIA_OFS_MIN, MACCEL_VIA_OFS_MAX);
#ifdef MACCEL_DEBUG
printf("MACCEL:via: tko: %.3f grw: %.3f OFS: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit);
#endif
Expand All @@ -66,8 +79,7 @@ void maccel_config_set_value(uint8_t *data) {
case id_maccel_limit: {
uint16_t limit = COMBINE_UINT8(value_data[0], value_data[1]);

// calc uint16 to float: offset moves comma, divides by 2 and shifts by 1, so that 1..14 fits into 0..60k
g_maccel_config.limit = (limit / 5000.0f) + 1;
g_maccel_config.limit = PROJECT_FROM_VIA(limit, MACCEL_VIA_LMT_MIN, MACCEL_VIA_LMT_MAX);
#ifdef MACCEL_DEBUG
printf("MACCEL:via: tko: %.3f grw: %.3f ofs: %.3f LMT: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit);
#endif
Expand All @@ -88,25 +100,26 @@ void maccel_config_get_value(uint8_t *data) {

switch (*value_id) {
case id_maccel_takeoff: {
uint16_t takeoff = (g_maccel_config.takeoff - 0.5) * 5000;
// uint16_t takeoff = (g_maccel_config.takeoff - 0.5) / (4.5 / 6) * 10000;
uint16_t takeoff = PROJECT_TO_VIA(g_maccel_config.takeoff, MACCEL_VIA_TKO_MIN, MACCEL_VIA_TKO_MAX);
value_data[0] = takeoff >> 8;
value_data[1] = takeoff & 0xFF;
break;
}
case id_maccel_growth_rate: {
uint16_t growth_rate = g_maccel_config.growth_rate * 10000;
uint16_t growth_rate = PROJECT_TO_VIA(g_maccel_config.growth_rate, MACCEL_VIA_GRO_MIN, MACCEL_VIA_GRO_MAX);
value_data[0] = growth_rate >> 8;
value_data[1] = growth_rate & 0xFF;
break;
}
case id_maccel_offset: {
uint16_t offset = (g_maccel_config.offset + 3) * 10000;
uint16_t offset = PROJECT_TO_VIA(g_maccel_config.offset, MACCEL_VIA_OFS_MIN, MACCEL_VIA_OFS_MAX);
value_data[0] = offset >> 8;
value_data[1] = offset & 0xFF;
break;
}
case id_maccel_limit: {
uint16_t limit = (g_maccel_config.limit - 1) * 5000;
uint16_t limit = PROJECT_TO_VIA(g_maccel_config.limit, MACCEL_VIA_LMT_MIN, MACCEL_VIA_LMT_MAX);
value_data[0] = limit >> 8;
value_data[1] = limit & 0xFF;
break;
Expand Down
Loading