¿Cómo calcular un ángulo a partir de puntos?

7 minutos de lectura

avatar de usuario de durumdara
durumdara

Quiero obtener una solución simple para calcular el ángulo de una línea (como el puntero de un reloj).

tengo 2 puntos:

cX, cY - the center of the line.
eX, eY - the end of the line.

The result is angle (0 <= a < 360).

¿Qué función es capaz de proporcionar este valor?

  • Necesitas al menos tres puntos para calcular un ángulo. ¿Cuál es tu 3ro? ¿El eje x o el eje y?

    – Sirko

    8 de marzo de 2012 a las 7:19

  • Ver también: Misma pregunta para Python

    – Martín Tomas

    20 de junio de 2020 a las 17:34

Avatar de usuario de Christian Mann
cristiano mann

Quieres el arcotangente:

dy = ey - cy
dx = ex - cx
theta = arctan(dy/dx)
theta *= 180/pi // rads to degs

Erm, tenga en cuenta que lo anterior obviamente no compila el código Javascript. Tendrás que mirar a través de la documentación para el arcotangente función.

Editar: Usando Matemáticas.atan2(y,x) manejará todos los casos especiales y la lógica adicional por usted:

función ángulo(cx, cy, ex, ey) { var dy = ey - cy;  var dx = ex - cx;  var theta = Math.atan2(dy, dx);  // rango (-PI, PI]theta *= 180 / Math.PI; // rads a grados, rango (-180, 180]//si (theta 

  • Note that to avoid division by 0, you should test if dx == 0 first; if it is, then return 90 degrees if dy > 0 and 270 degrees if dy < 0.

    – Etienne Perot

    Mar 8, 2012 at 7:20

  • @EtiennePerot: I remembered another useful function and updated my answer to use that instead.

    – Christian Mann

    Mar 8, 2012 at 7:24

  • You may also want to convert the result to degrees, it sounds like that's what the OP wants.

    – Etienne Perot

    Mar 8, 2012 at 7:26

  • Almost, there is a little error in it: theta*=180/Math.PI is correct. You also don't need to worry about zero division, when using atan2(), it returns correct value with 0 too.

    – Teemu

    Mar 8, 2012 at 7:36

  • Added (optional) code to flip it to 360 range as the question asked.

    – TWiStErRob

    Jun 30, 2015 at 11:12

TWiStErRob's user avatar
TWiStErRob

Runnable version of Christian's answer.

function angle(cx, cy, ex, ey) {
  var dy = ey - cy;
  var dx = ex - cx;
  var theta = Math.atan2(dy, dx); // range (-PI, PI]
  theta *= 180 / Math.PI;  // rads a grados, range (-180, 180]return theta; } function angle360(cx, cy, ex, ey) { var theta = angle(cx, cy, ex, ey); // range (-180, 180]si (theta 
table {
  border-collapse: collapse;
}
table, th, td {
  border: 1px solid black;
  padding: 2px 4px;
}
tr > td:not(:first-child) {
  text-align: center;
}
tfoot {
  font-style: italic;
}
<table>
  <thead>
    <tr><th>Direction*</th><th>Start</th><th>End</th><th>Angle</th><th>Angle 360</th></tr>
  </thead>
  <tfoot>
     <tr><td colspan="5">* Cartesian coordinate system<br>positive x pointing right, and positive y pointing up.</td>
  </tfoot>
  <tbody id="angles">
  </tbody>
</table>
<script>
function show(label, cx, cy, ex, ey) {
  var row = "<tr>";
  row += "<td>" + label + "</td>";
  row += "<td>" + [cx, cy] + "";  fila += "" + [ex, ey] + "";  fila += "" + ángulo(cx, cy, ex, ey) + "";  fila += "" + ángulo360(cx, cy, ex, ey) + "";  fila += "";  document.getElementById("ángulos").innerHTML += fila;  } 

Si está utilizando lienzo, notará (si aún no lo ha hecho) que el lienzo usa la rotación en el sentido de las agujas del reloj(MDN) y y se invierte el eje. Para obtener resultados consistentes, necesita ajustar su angle función.

De vez en cuando, necesito escribir esta función y cada vez necesito buscarla, porque nunca llego al final del cálculo.

Si bien las soluciones sugeridas funcionan, no tienen en cuenta el sistema de coordenadas del lienzo. Examine la siguiente demostración:

Calcular ángulo a partir de puntos - JSFiddle

function angle(originX, originY, targetX, targetY) {
    var dx = originX - targetX;
    var dy = originY - targetY;

    // var theta = Math.atan2(dy, dx);  // [0, Ⲡ] then [-Ⲡ, 0]; clockwise; 0° = west
    // theta *= 180 / Math.PI;          // [0, 180] then [-180, 0]; clockwise; 0° = west
    // if (theta < 0) theta += 360;     // [0, 360]; clockwise; 0° = west

    // var theta = Math.atan2(-dy, dx); // [0, Ⲡ] then [-Ⲡ, 0]; anticlockwise; 0° = west
    // theta *= 180 / Math.PI;          // [0, 180] then [-180, 0]; anticlockwise; 0° = west
    // if (theta < 0) theta += 360;     // [0, 360]; anticlockwise; 0° = west

    // var theta = Math.atan2(dy, -dx); // [0, Ⲡ] then [-Ⲡ, 0]; anticlockwise; 0° = east
    // theta *= 180 / Math.PI;          // [0, 180] then [-180, 0]; anticlockwise; 0° = east
    // if (theta < 0) theta += 360;     // [0, 360]; anticlockwise; 0° = east

    var theta = Math.atan2(-dy, -dx); // [0, Ⲡ] then [-Ⲡ, 0]; clockwise; 0° = east
    theta *= 180 / Math.PI;           // [0, 180] then [-180, 0]; clockwise; 0° = east
    if (theta < 0) theta += 360;      // [0, 360]; clockwise; 0° = east

    return theta;
}

  • ¿Qué significa cuando tengo que sumar 90 grados a la respuesta para obtener la rotación correcta?

    – 1,21 gigavatios

    15 de agosto de 2019 a las 21:37


  • Además, ¿cuáles son los nombres de los sistemas de coordenadas? Movimiento de coordenadas cartesianas hacia la derecha y hacia arriba en.wikipedia.org/wiki/Cartesian_coordinates. ¿Cómo se llama cuando los valores positivos se mueven hacia la derecha y hacia abajo?

    – 1,21 gigavatios

    15 de agosto de 2019 a las 21:41

  • @ 1,21 gigavatios ¿Me estás citando con "hay que sumar 90 grados a la respuesta para obtener la rotación correcta" (porque no dije tal cosa) ¿o es otra cosa y estás tratando de encontrarle sentido? Hmm, no estoy seguro si tiene un nombre. La única diferencia entre los dos es la dirección de la rotación. Canvas también utiliza el sistema de coordenadas cartesianas. Es solo en el sentido de las agujas del reloj en lugar de en el sentido contrario a las agujas del reloj. Examine la sección "Cuadrantes y octantes" en la página que vinculó. voltear el y eje, eso es cambiar I y II cuadrantes con IV y III, respectivamente. Ahora, es en el sentido de las agujas del reloj.

    – akinuri

    16 de agosto de 2019 a las 7:25


  • Estoy tratando de entender por qué agregar 90 grados al resultado funciona para mi caso. Acabo de notar en la respuesta de @pasx que tiene un comentario. a -= (Math.PI/2); //shift by 90deg y he visto mención de 90 grados en otros lugares.

    – 1,21 gigavatios

    16 de agosto de 2019 a las 14:33


  • @ 1,21 gigavatios, lo mismo. Desviación de 90°.

    –Jeff Fischer

    24 de septiembre de 2020 a las 17:34

Uno de los problemas para obtener el ángulo entre dos puntos o cualquier ángulo es la referencia que usa.

En matemáticas usamos un círculo trigonométrico con el origen a la derecha del círculo (un punto en x=radio, y=0) y contamos el ángulo en sentido antihorario de 0 a 2PI.

En geografía el origen es el Norte a 0 grados y vamos en el sentido de las agujas del reloj desde a 360 grados.

El siguiente código (en C#) obtiene el ángulo en radianes y luego lo convierte en un ángulo geográfico:

    public double GetAngle()
    {
        var a = Math.Atan2(YEnd - YStart, XEnd - XStart);
        if (a < 0) a += 2*Math.PI; //angle is now in radians

        a -= (Math.PI/2); //shift by 90deg
        //restore value in range 0-2pi instead of -pi/2-3pi/2
        if (a < 0) a += 2*Math.PI;
        if (a < 0) a += 2*Math.PI;
        a = Math.Abs((Math.PI*2) - a); //invert rotation
        a = a*180/Math.PI; //convert to deg

        return a;
    }

Aquí encuentras dos fórmulas, una desde el eje x positivo y en sentido contrario a las agujas del reloj

y uno desde el norte y en el sentido de las agujas del reloj.

Hay x=x2-x1 y y=y2=y1. Hay E=E2-E1 y N=N2-N1.

Las fórmulas funcionan para cualquier valor de x, y, E y N.

Para x=y=0 o E=N=0 el resultado no está definido.

f(x,y)=pi()-pi()/2*(1+signo(x))*(1-signo(y^2))

     -pi()/4*(2+sign(x))*sign(y)

     -sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))

f(E,N)=pi()-pi()/2*(1+signo(N))*(1-signo(E^2))

     -pi()/4*(2+sign(N))*sign(E)

     -sign(E*N)*atan((abs(N)-abs(E))/(abs(N)+abs(E)))

¿Ha sido útil esta solución?