Pseudoangles: Difference between revisions

From vegard.wiki
Jump to navigation Jump to search
Content added Content deleted
(typo)
(add new code variant)
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[File:atan.png|420px|thumb|Field lines for regular atan.]]
[[File:pseudoangles.png|420px|thumb|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 <tt>atan()</tt> or <tt>atan2()</tt>.
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 <tt>atan()</tt> or <tt>atan2()</tt>.


Line 12: Line 15:
</source>
</source>


This function returns a value between 1 and 5. When using as a sort key, this yields the same behaviour as the key <tt>mod(atan2(y, x) + 1.5 * pi, 2. * pi)</tt>. It is therefore not a direct replacement for <tt>atan2()</tt> if you care about which angles are considered the smallest/larges, however it does sort in the correct "direction".
This function returns a value between 1 and 5. When used as a sort key, this yields the same behaviour as the key <tt>mod(atan2(y, x) + 1.5 * pi, 2. * pi)</tt>. It is therefore not a direct replacement for <tt>atan2()</tt> 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 <tt>atan2()</tt> (with the return value in range 0-4), you can use:
If you want to preserve the exact sorting order as if you had used <tt>atan2()</tt> (with the return value in range 0-4), you can use:


<source lang="Python">
<source lang="Python">
def psuedoangle(y, x):
def pseudoangle(y, x):
r = x / (abs(x) + abs(y))
r = x / (abs(x) + abs(y))
if y < 0:
if y < 0:
Line 27: Line 30:


<source lang="Python">
<source lang="Python">
def psuedoangle(y, x):
def pseudoangle(y, x):
r = x / (abs(x) + abs(y))
r = x / (abs(x) + abs(y))
if y < 0:
if y < 0:
Line 35: Line 38:


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

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

<source lang="GLSL">
float pseudoangle(float y, float x)
{
return sign(y) * (1. - x / (abs(x) + abs(y)));
}
</source>

=== See also ===

* https://stackoverflow.com/questions/16542042/fastest-way-to-sort-vectors-by-angle-without-actually-computing-that-angle


[[Category:Programming]]
[[Category:Programming]]
[[Category:Geometry]]
[[Category:Geometry]]
[[Category:Trigonometry]]

Latest revision as of 18:19, 25 February 2020

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