Skip to content

Conversation

rygwdn
Copy link
Contributor

@rygwdn rygwdn commented Feb 14, 2025

  • Determine the mouse acceleration by using the 2d magnitude of the movement. This keeps diagonal movements more consistent.
  • Use linear interpolation on the acceleration curve to smoothly scale the acceleration
  • Use floating point math when applying the scaling factor. This makes sense because it will also be multiplied by current->speed_{x,y} values which are typically greater than 1 and so this retains the fractional acceleration factors further through the calculations. i.e. if speed_x is 10, then previously then the acceleration curve could only adjust the movement by a multiple of 10, now it can always adjust down to 1.

@rygwdn rygwdn mentioned this pull request Feb 18, 2025
@hrvach
Copy link
Owner

hrvach commented Mar 9, 2025

Floating point is usually slow on rp2040, but the difference shouldn't be that significant. I might try an alpha max + beta min approach just out of curiosity.

@hrvach hrvach merged commit 00e109c into hrvach:main Mar 9, 2025
1 check passed
@hrvach
Copy link
Owner

hrvach commented Mar 10, 2025

@rygwdn tried to accelerate the float operations a bit, can you test if this change still works as you expect?

#define Q10(x) (x<<10)

float calculate_mouse_acceleration_factor(int32_t offset_x, int32_t offset_y) {    
    const int32_t abs_x = abs(offset_x);
    const int32_t abs_y = abs(offset_y);
    const float q10tof = 1.0f / 1024.0f;

    const struct curve {
        int value;  /* In Q22.10 */
        float factor;
        float slope;
    } acceleration[ACCEL_POINTS] = {
                                        // 4 |                                        *
        {Q10(2), 1, 0.02 * q10tof},     //   |                                  *
        {Q10(5), 1.1, 0.03 * q10tof},   // 3 |
        {Q10(15), 1.4, 0.03 * q10tof},  //   |                       *
        {Q10(30), 1.9, 0.033 * q10tof}, // 2 |                *
        {Q10(45), 2.6, 0.046 * q10tof}, //   |        *
        {Q10(60), 3.4, 0.053 * q10tof}, // 1 |  *
        {Q10(70), 4.0, 0.060 * q10tof}, //    -------------------------------------------
    };                                  //        10    20    30    40    50    60    70

    if (offset_x == 0 && offset_y == 0)
        return 1.0;

    if (!global_state.config.enable_acceleration)
        return 1.0;

    /* RP2040 float is slow, so instead of sqrtf(off_x^2 + off_y^2), 
       doing alpha max + beta min in Q22.10 with max error of ~ 3.96%.
       alpha = 0.9604, beta = 0.3978, mean error 2.41%
       https://en.wikipedia.org/wiki/Alpha_max_plus_beta_min_algorithm       
    */
    const int movement_magnitude = 983 * MAX(abs_x, abs_y) + 407 * MIN(abs_x, abs_y);

    if (movement_magnitude <= acceleration[0].value)
        return acceleration[0].factor;

    if (movement_magnitude >= acceleration[ACCEL_POINTS-1].value)
        return acceleration[ACCEL_POINTS-1].factor;

    const struct curve *lower = NULL;
    const struct curve *upper = NULL;

    for (int i = 0; i < ACCEL_POINTS-1; i++) {
        if (movement_magnitude < acceleration[i + 1].value) {
            lower = &acceleration[i];
            upper = &acceleration[i + 1];
            break;
        }
    }

    // Should never happen, but just in case
    if (lower == NULL || upper == NULL)
        return 1.0;

    return lower->factor + (movement_magnitude - lower->value) * upper->slope;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants