ChatGPT解决这个技术问题 Extra ChatGPT

How can I find the difference between two angles?

Given 2 angles in the range -PI -> PI around a coordinate, what is the value of the smallest of the 2 angles between them?

Taking into account that the difference between PI and -PI is not 2 PI but zero.

Example:

Imagine a circle, with 2 lines coming out from the center, there are 2 angles between those lines, the angle they make on the inside aka the smaller angle, and the angle they make on the outside, aka the bigger angle. Both angles when added up make a full circle. Given that each angle can fit within a certain range, what is the smaller angles value, taking into account the rollover

I read 3 times before I understood what you meant. Please add an example, or explain better...
Imagine a circle, with 2 lines comign out from the center, there are 2 angles between those lines, the angle they make on the inside aka the smaller angle, and the angle they make on the outside, aka the bigger angle. Both angles when added up make a full circle. Given that each angle can fit within a certain range, what is the smaller angles value, taking into account the rollover
@JimG. this isn't the same question, in this question the angle P1 used in the other question would be the incorrect answer, it would be the other, smaller angle. Also, there is no guarantee that the angle is with the horizontal axis
if you use Unity c# script, you can use Mathf.DeltaAngle function.

b
bennedich

This gives a signed angle for any angles:

a = targetA - sourceA
a = (a + 180) % 360 - 180

Beware in many languages the modulo operation returns a value with the same sign as the dividend (like C, C++, C#, JavaScript, full list here). This requires a custom mod function like so:

mod = (a, n) -> a - floor(a/n) * n

Or so:

mod = (a, n) -> (a % n + n) % n

If angles are within [-180, 180] this also works:

a = targetA - sourceA
a += (a>180) ? -360 : (a<-180) ? 360 : 0

In a more verbose way:

a = targetA - sourceA
a -= 360 if a > 180
a += 360 if a < -180

although one might want to do a % 360, e.g. if I had the angle 0 and the target angle 721, the correct answer would be 1, the answer given by the above would be 361
A more concise, though potentially more expensive, equivalent of the latter approach's second statement, is a -= 360*sgn(a)*(abs(a) > 180). (Come to think of it, if you've branchless implementations of sgn and abs, then that characteristic might actually start to compensate for needing two multiplications.)
The "Signed angle for any angle" example seems to work in most scenarios, with one exception. In scenario double targetA = 2; double sourceA = 359; 'a' will be equal to -357.0 instead of 3.0
In C++ you can use std::fmod(a,360), or fmod(a,360) to use floating point modulo.
If the % operator acts like remainder in your language (retains sign), you can simply add an extra 360 instead of defining a modulus function: a = (a + 540) % 360 - 180 As stated above, this only works for angles within 360 of each other, which may often be the case. Otherwise: a = ((a % 360) + 540) % 360 - 180
P
Peter B

x is the target angle. y is the source or starting angle:

atan2(sin(x-y), cos(x-y))

It returns the signed delta angle. Note that depending on your API the order of the parameters for the atan2() function might be different.


x-y gives you the difference in angle, but it may be out of the desired bounds. Think of this angle defining a point on the unit circle. The coordinates of that point are (cos(x-y), sin(x-y)). atan2 returns the angle for that point (which is equivalent to x-y) except its range is [-PI, PI].
a one line simple solution and solved for me(not the selected answer ;) ). but tan inverse is a costly process.
For me, the most elegant solution. Shame it might be computationally expensive.
For me the most elegant solution as well! Solved my problem perfectly (wanted to have a formula that gives me the signed turn angle which is the smaller one from the two possible turn directions/angles).
L
Laurence Gonsalves

If your two angles are x and y, then one of the angles between them is abs(x - y). The other angle is (2 * PI) - abs(x - y). So the value of the smallest of the 2 angles is:

min((2 * PI) - abs(x - y), abs(x - y))

This gives you the absolute value of the angle, and it assumes the inputs are normalized (ie: within the range [0, 2π)).

If you want to preserve the sign (ie: direction) of the angle and also accept angles outside the range [0, 2π) you can generalize the above. Here's Python code for the generalized version:

PI = math.pi
TAU = 2*PI
def smallestSignedAngleBetween(x, y):
    a = (x - y) % TAU
    b = (y - x) % TAU
    return -a if a < b else b

Note that the % operator does not behave the same in all languages, particularly when negative values are involved, so if porting some sign adjustments may be necessary.


@bradgonesurfing That is/was true, but to be fair your tests checked for things that weren't specified in the original question, specifically non-normalized inputs and sign-preservation. The second version in the edited answer should pass your tests.
The second version also doesn't work for me. Try 350 and 0 for example. It should return -10 but returns -350
@kjyv I can't reproduce the behavior you describe. Can you post the exact code?
Ah, I'm sorry. I've tested exactly your version with rad and degrees in python again and it worked fine. So must have been a mistake in my translation to C# (don't have it anymore).
Note that, as of Python 3, you can actually use tau natively! Just write from math import tau.
D
David Jones

I rise to the challenge of providing the signed answer:

def f(x,y):
  import math
  return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)

Ah... the answer is a Python function by the way. Sorry, I was in Python mode for a moment. Hope that's okay.
I shall plug the new formula into my code upstairs and see what becomes of it! ( thankyou ^_^ )
I'm pretty sure PeterB's answer is correct too. And evilly hackish. :)
But this one contains no trig functions :)
What is the equivalent formula for java? if angles are in degree.?
A
Adriel Jr

An efficient code in C++ that works for any angle and in both: radians and degrees is:

inline double getAbsoluteDiff2Angles(const double x, const double y, const double c)
{
    // c can be PI (for radians) or 180.0 (for degrees);
    return c - fabs(fmod(fabs(x - y), 2*c) - c);
}

See it working here: https://www.desmos.com/calculator/sbgxyfchjr


J
Josh

For UnityEngine users, the easy way is just to use Mathf.DeltaAngle.


Has no signed output tho
R
Rudolf Meijering

Arithmetical (as opposed to algorithmic) solution:

angle = Pi - abs(abs(a1 - a2) - Pi);

Fails if abs(a1-a2) >>> 360 . Use this instead: stackoverflow.com/a/52432897/6050364
E
Eduard

A simple method, which I use in C++ is:

double deltaOrientation = angle1 - angle2;
double delta =  remainder(deltaOrientation, 2*M_PI);

This is wrong, I'm afraid. Consider if angle1 = 0 and angle2 = pi+c, for some c>0. The correct answer should be -(pi-c), but your answer gives pi+c. Bear in mind that the OP explicitly asked for the smaller angle, and the smaller angle should always be less than or equal to pi.
t
tm_lv

I absolutely love Peter B's answer above, but if you need a dead simple approach that produces the same results, here it is:

function absAngle(a) { // this yields correct counter-clock-wise numbers, like 350deg for -370 return (360 + (a % 360)) % 360; } function angleDelta(a, b) { // https://gamedev.stackexchange.com/a/4472 let delta = Math.abs(absAngle(a) - absAngle(b)); let sign = absAngle(a) > absAngle(b) || delta >= 180 ? -1 : 1; return (180 - Math.abs(delta - 180)) * sign; } // sample output for (let angle = -370; angle <= 370; angle+=20) { let testAngle = 10; console.log(testAngle, "->", angle, "=", angleDelta(testAngle, angle)); }

One thing to note is that I deliberately flipped the sign: counter-clockwise deltas are negative, and clockwise ones are positive


U
Uli Gue

There is no need to compute trigonometric functions. The simple code in C language is:

#include <math.h>
#define PIV2 M_PI+M_PI
#define C360 360.0000000000000000000
double difangrad(double x, double y)
{
double arg;

arg = fmod(y-x, PIV2);
if (arg < 0 )  arg  = arg + PIV2;
if (arg > M_PI) arg  = arg - PIV2;

return (-arg);
}
double difangdeg(double x, double y)
{
double arg;
arg = fmod(y-x, C360);
if (arg < 0 )  arg  = arg + C360;
if (arg > 180) arg  = arg - C360;
return (-arg);
}

let dif = a - b , in radians

dif = difangrad(a,b);

let dif = a - b , in degrees

dif = difangdeg(a,b);

difangdeg(180.000000 , -180.000000) = 0.000000
difangdeg(-180.000000 , 180.000000) = -0.000000
difangdeg(359.000000 , 1.000000) = -2.000000
difangdeg(1.000000 , 359.000000) = 2.000000

No sin, no cos, no tan,.... only geometry!!!!


Bug! Since you #define PIV2 as "M_PI+M_PI", not "(M_PI+M_PI)", the line arg = arg - PIV2; expands to arg = arg - M_PI + M_PI, and so does nothing.