Como mapear atan2 () para graus 0-360

108

atan2(y, x) tem essa descontinuidade em 180 ° onde muda para -180 ° ..0 ° no sentido horário.

Como mapeio a faixa de valores para 0 ° ..360 °?

aqui está meu código:

CGSize deltaPoint = CGSizeMake(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
float swipeBearing = atan2f(deltaPoint.height, deltaPoint.width);

Estou calculando a direção de um evento de toque de deslizar considerando as duas estruturas de ponto XY startPointe e endPoint. O código é para o iPhone, mas qualquer idioma que suporte serve atan2f().

willc2
fonte
Nota: O método de atualização postado não retornará zero graus, mas valores logo acima de 0 a 360,0.
chux - Reintegrar Monica em
2
> [Como obter o ângulo de 2 posições] [1] [1]: stackoverflow.com/questions/9457988/…
MAnoj Sarnaik
Esta função funciona muito bem, no entanto, o ângulo de cálculo de "bearingDegrees" é invertido. por exemplo, 45 graus normalmente seriam no primeiro quadrante, porém isso no 4o quadrante. 135 graus normalmente seriam no segundo quadrante, mas esta função retorna para o terceiro quadrante. Posso simplesmente pegar o valor de retorno da função x e negar isso de 360 ​​para obter o valor do ângulo correto, mas estou curioso para saber por que isso está acontecendo em primeiro lugar?
goelv

Respostas:

66
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
Erikkallen
fonte
6
Provavelmente também deseja x> = 0 para o caso x = 0.
bpw1621
13
Para aqueles que não estão confortáveis ​​com esta notação, e sem a conversão para graus embutida em: if (x> 0) {radians = x;} else {radians = 2 * PI + x;} então estamos apenas adicionando 2PI ao resultado se é menor que 0.
David Doria
1
Ou (x >= 0 ? x : (2*PI + x)) * 180/PIcomo em(x < 0 ? 2*PI + x : x) * 180/PI
user3342816
97

Solução usando Módulo

Uma solução simples que abrange todos os casos.

degrees = (degrees + 360) % 360;  // +360 for implementations where mod returns negative numbers

Explicação

Positivo: 1 a 180

Se você modificar qualquer número positivo entre 1 e 180 por 360, obterá exatamente o mesmo número inserido. O modo aqui apenas garante que esses números positivos sejam retornados com o mesmo valor.

Negativo: -180 a -1

Usar o mod aqui retornará valores na faixa de 180 e 359 graus.

Casos especiais: 0 e 360

Usar mod significa que 0 é retornado, tornando esta uma solução segura de 0-359 graus.

Liam George Betsworth
fonte
3
soluções incríveis :)
Qadir Hussain
5
Não acredito que seja necessário adicionar 360. -1% 360 ainda é 359 :)
pleasemorebacon
11
Não acho que isso seja correto em todos os idiomas. Em Javascript-1 % 360 = -1
Startec
Também não é uma abordagem viável em Java
Hulk
1
@pleasemorebacon Incorreto. Em alguns idiomas -1% 360 é -1.
Pharap
40

Basta adicionar 360 ° se a resposta de atan2 for menor que 0 °.

dave4420
fonte
6
O que é o mesmo que "apenas adicionar 2 * PI" se você estiver tendo um daqueles dias.
Chris O,
33

Ou se você não gosta de ramificação, apenas negue os dois parâmetros e adicione 180 ° à resposta.

(Adicionar 180 ° ao valor de retorno o coloca perfeitamente na faixa de 0-360, mas inverte o ângulo. Negar os dois parâmetros de entrada o inverte.)

aib
fonte
2
Obrigado, isso é exatamente o que eu estava procurando.
Jeremy Herrman
2
Prefiro modificar meu código para usar ângulos desnormalizados (<0,> = 360), mas sempre parece haver alguém que busca aquela falsa sensação de "otimizado"; é por isso que eu queria adicionar isso. (Ou foi porque esta foi a maneira mais rápida de contornar algum código de depuração temporário que usei? Hmm)
aib
1
Definitivamente não é fácil de grocar, como posso concordar depois de 2 anos ou mais. Portanto: Adicionar 180 ° ao valor de retorno o coloca perfeitamente no intervalo 0-360, mas inverte o ângulo. Negar ambos os parâmetros de entrada o reverte.
aib
Isso pode ter alguns problemas quando $ x = 0 $ e $ y> 0 $ iirc
Trinidad
22

@erikkallen está perto, mas não muito bem.

theta_rad = atan2(y,x);
theta_deg = (theta_rad/M_PI*180) + (theta_rad > 0 ? 0 : 360);

Isso deve funcionar em C ++: (dependendo de como o fmod é implementado, pode ser mais rápido ou mais lento do que a expressão condicional)

theta_deg = fmod(atan2(y,x)/M_PI*180,360);

Alternativamente, você pode fazer isso:

theta_deg = atan2(-y,-x)/M_PI*180 + 180;

uma vez que (x, y) e (-x, -y) diferem em ângulos em 180 graus.

Jason S
fonte
se entendi corretamente em Fortran, é atan2 (-y, -x) * 180 / PI + 180. Correto?
gansub
desculpe, eu não sei FORTRAN. Mas sua matemática parece certa.
Jason S de
11

Tenho 2 soluções que parecem funcionar para todas as combinações de xe y positivo e negativo.

1) Abuso atan2 ()

De acordo com os documentos, atan2 usa os parâmetros y e x nessa ordem. No entanto, se você os inverter, poderá fazer o seguinte:

double radians = std::atan2(x, y);
double degrees = radians * 180 / M_PI;
if (radians < 0)
{
    degrees += 360; 
}

2) Use atan2 () corretamente e converta depois

double degrees = std::atan2(y, x) * 180 / M_PI;
if (degrees > 90)
{
    degrees = 450 - degrees;
}
else
{
    degrees = 90 - degrees;
}
Fibbles
fonte
Você, senhor, é um salvador de vidas. Acabei de usar essa abordagem no Unity e funciona como um encanto.
porfiriopartida
7

@Jason S: sua variante "fmod" não funcionará em uma implementação compatível com os padrões. O padrão C é explícito e claro (7.12.10.1, "as funções fmod"):

se y for diferente de zero, o resultado terá o mesmo sinal de x

portanto,

fmod(atan2(y,x)/M_PI*180,360)

é na verdade apenas uma reescrita detalhada de:

atan2(y,x)/M_PI*180

Sua terceira sugestão, no entanto, está correta.

Stephen Canon
fonte
5

Isso é o que eu normalmente faço:

float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;
andrés
fonte
2
angle = Math.atan2(x,y)*180/Math.PI;

Eu fiz uma fórmula para orientar o ângulo de 0 a 360

angle + Math.ceil( -angle / 360 ) * 360;
Saad Ahmed
fonte
2

Uma solução alternativa é usar a função mod () definida como:

function mod(a, b) {return a - Math.floor (a / b) * b;}

Então, com a seguinte função, o ângulo entre os pontos ini (x, y) e final (x, y) é obtido. O ângulo é expresso em graus normalizados para [0, 360] graus. e Norte referenciando 360 graus.

    function angleInDegrees(ini, end) {
        var radian = Math.atan2((end.y - ini.y), (end.x - ini.x));//radian [-PI,PI]
        return mod(radian * 180 / Math.PI + 90, 360);
    }
A. Torrez Garay
fonte
1

A geosfera dos pacotes R calculará o rolamentoRhumb, que é uma linha de rolamento constante dado um ponto de origem e leste / norte. O leste e o norte devem estar em uma matriz ou vetor. O ponto de origem de uma rosa dos ventos é 0,0. O código a seguir parece resolver prontamente o problema:

windE<-wind$uasE
windN<-wind$vasN
wind_matrix<-cbind(windE, windN)
wind$wind_dir<-bearingRhumb(c(0,0), wind_matrix)
wind$wind_dir<-round(wind$wind_dir, 0)
Ariane
fonte
1
theta_rad = Math.Atan2(y,x);
if(theta_rad < 0)
  theta_rad = theta_rad + 2 * Math.PI;    //if neg., add 2 PI to it
theta_deg = (theta_rad/M_PI*180) ;        //convert from radian to degree

//or
theta_rad = Math.Atan2(y,x);
theta_rad = (theta_rad < 0) ? theta_rad + 2 * Math.PI : theta_rad;
theta_deg = (theta_rad/M_PI*180) ;

-1 grau torna-se (-1 + 360) = 359 graus
-179 graus torna-se (-179 + 360) = 181 graus

PhilC
fonte
O que é Math.PI? É o mesmo que M_PI?
Pang
1
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);

Isso retornará o grau de 0 ° -360 ° no sentido anti-horário, 0 ° está nas 3 horas.

Finallz
fonte
1

Uma fórmula para ter o intervalo de valores de 0 a 360 graus.

f (x, y) = 180-90 * (1 + sinal (x)) * (1-sinal (y ^ 2)) - 45 * (2 + sinal (x)) * sinal (y)

     -(180/pi())*sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
Teodoro Panagos
fonte
Você pode explicar como isso se relaciona com a pergunta?
Klaus Gütter
1

Aqui está um pouco de javascript. Basta inserir os valores xey.

var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
quadrado
fonte