Pseudoangles

From vegard.wiki
Revision as of 18:19, 25 February 2020 by Vegard (talk | contribs) (add new code variant)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
Field lines for regular atan.
Field lines for pseudoangles. Note that the sectors near the cardinal directions are slightly narrower compared to regular atan.

Pseudoangles allow you to compare the angles of two vectors without actually computing the angle itself (which could be costly). By extension, it also allows you to sort a list of vectors by angle without calling atan() or atan2().

The version you'll typically find on Google [1][2] is basically:

def pseudoangle(y, x):
    r = y / (abs(x) + abs(y))
    if x < 0:
        return 2. - r
    else:
        return 4. + r

This function returns a value between 1 and 5. When used as a sort key, this yields the same behaviour as the key mod(atan2(y, x) + 1.5 * pi, 2. * pi). It is therefore not a direct replacement for atan2() if you care about which angles are considered the smallest/larges, however it does sort in the correct "direction".

If you want to preserve the exact sorting order as if you had used atan2() (with the return value in range 0-4), you can use:

def pseudoangle(y, x):
    r = x / (abs(x) + abs(y))
    if y < 0:
        return 1. + r
    return 3. - r

If you also want the sign of the return value to be the same as for atan2(), i.e. return value in range (-2 to 2), you can use:

def pseudoangle(y, x):
    r = x / (abs(x) + abs(y))
    if y < 0:
        return r - 1.
    return 1. - r

I personally prefer this last version as there are no performance disadvantages to using it anyway.

In languages with a sign() (or copysign()) function you can use that to further simplify it:

float pseudoangle(float y, float x)
{
    return sign(y) * (1. - x / (abs(x) + abs(y)));
}

See also