0
\$\begingroup\$

I want to make a plane controller using SDL2. I want to use one joystick for pitch/roll. Y-axis sets the pitch-flaps, and X-axis sets the roll-flaps.

But there is one problem: Say I want to fully pitch and roll at the same time: I will hold the joystick at a 45 degree angle. But in SDL2, the joyAxis then get reported as 0.707... which is roughly half of the square root of 2. I understand why this happens: the physical joystick is restricted within a circle, so to move it up, you need to sacrifice a little bit of left/right. So the magnitude of the input vector is at most 1, instead of sqrt(2). How do I correct for this in code? I can't think of a good mapping.

I would like something like this:

float rawInput[2] = {0.707, 0.707};
float actualInput[2] = squaredCircle(rawInput); // {1,1}

rawInput = {1, 0};
actualInput = squaredCircle(rawInput); // {1,0}

What is a good squaredCircle function?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

If the angle is 45° + 90°k, we want to multiply by sqrt(2). When the angle is just 90°k, we want to multiply by 1.

Thus, linearly interpolate between 1 and sqrt(2) based on abs( atan(x/y) - 45° )/45, then scale the input by that value.

This works because atan(x/y) gives a value in the domain [0°, 90°>. We then subtract 45° to get a range of [-45°, 45°>, which we then mirror with abs() to [45°, 0, 45°>. We then normalize it to [0,1>.

Because division by 0 is undefined, we will manually check that y is not 0. Since this implies a 90° angle, it also solves our incomplete domain. We now have a domain of [0,1].

#include <math.h>
#include <stdio.h>

#ifndef M_PI
#define M_PI 3.141592654
#endif

#define SQRT2 1.414213562

#define NEAR_ZERO 0.000001

typedef struct vec2f_t {float x,y;} vec2f;
#define vec2f(x,y)  (vec2f){x,y}
#define printv2(v2) printf(#v2"=vec2(%f, %f)\n",v2.x,v2.y)

vec2f squaredCircle(vec2f in)
{
    if (fabs(in.x) < NEAR_ZERO){
        return in; // multiple of 90 degs so don't scale
    }

    float t = fabs( atan(in.x/in.y) - M_PI/4 ) * (4/M_PI); // a/(b/c) = a*(c/b) 
    // t = 1 when multiple of 90 deg
    // t = 0 when multiple of 90 deg + 45 deg
    
    // scale = (sqrt(2.0) - 1.0)*(1.0-t) + 1.0;
    // (a-1)*(1-b) + 1 = a + a*-b + -1*1 + -1*-b +1
    //                 = a - ab +b -1 +1
    //                 = a*(1 - b) + b

    float s = SQRT2*(1.0-t) + t;

    return (vec2f){s*in.x, s*in.y};
}

int main(){
    vec2f in = vec2f(1.0, 0.0);
    in = squaredCircle(in);
    printv2(in);

    in = squaredCircle(vec2f(0.707, 0.707));
    printv2(in);

    in = squaredCircle(vec2f(0.5, 0.5));
    printv2(in);

    in = squaredCircle(vec2f(0.0, 1.0));
    printv2(in);
}
\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .