La arcotangente

facebook-svg gplus-svg twitter-svg

En trigonometría, la arcotangente se define como la función inversa de la tangente de un ángulo, y su significado geométrico es el arco y ( en radianes ) cuya tangente es tan.
En JavaScript tenemos dos métodos que devuelven la arcotangente:
- Math.atan(tan), donde tan es el valor de la tangente ( y/x ) y
- Math.atan2(y,x), donde x e y son las coordenadas (relativas) de un punto.
¡Ojo! El primer argumento de atan2 es y, y el segundo argumento es x.

Los dos métodos devuelven el valor del arcotangente en radianes!! Pero ¿por qué necesitamos dos métodos?

Math.atan versus Math.atan2

Algunos aclaramientos: en el siguiente ejemplo

  • el punto ( cx,cy ) esta situado en el centro del <canvas>.
  • El ratón esta situado en el punto m ( m.x, m.y ).
  • La variable deltaX representa la distancia en x entre el centro cx y el ratón m.x : var deltaX = m.x - cx;
  • La variable deltaY representa la distancia en y entre el centro cy y el ratón m.y : var deltaY = m.y - cy;

En el primer cuadrante ( de 0º a 90º )

  • deltaX > 0;
  • deltaY > 0;
  • el valor del ángulo calculado con Math.atan(deltaY/ deltaX) > 0

En el segundo cuadrante ( de 90º a 180º )

  • deltaX > 0;
  • deltaY < 0;
  • el valor del ángulo calculado con Math.atan(deltaY/ deltaX) < 0

En el tercer cuadrante ( de 180º a 270º )

  • deltaX < 0;
  • deltaY < 0;
  • el valor del ángulo calculado con Math.atan(deltaY/ deltaX) > 0

Finalmente en el cuarto cuadrante ( de 270º a 360º )

  • deltaX > 0;
  • deltaY < 0;
  • el valor del ángulo calculado con Math.atan(deltaY/ deltaX) < 0

Si queremos encontrar el valor del ángulo Math.atan() es el método que tenemos que utilizar, pero muchas veces necesitamos saber el cuadrante en el cual nos encontramos. Para esto hay que utilizar Math.atan2(deltaY, deltaX).

Todo esto esta ilustrado en el siguiente ejemplo. Para que las cosas sean más claras los resultados de Math.atan() y Math.atan2() han sido transformados en grados sexagesimales.

Math.atan vs Math.atan2

Por favor arrastre el punto naranja.

0deg

Math.atan : 0.00

Math.atan2: 0.00

var deltaX = m.x - cx; 0.00

var deltaY = m.y - cy; 0.00


Math.atan vs Math.atan2

Por favor arrastre el punto naranja.

0deg

Math.atan : 0.00

Math.atan2: 0.00

var deltaX = m.x - cx; 0.00

var deltaY = m.y - cy; 0.00


var output = document.getElementById("output");
var Atan = document.getElementById("atan");
var Atan2 = document.getElementById("atan2");
var deltax = document.getElementById("deltax");
var deltay = document.getElementById("deltay");
var c = document.getElementById("c");
var ctx = c.getContext("2d");

var cw = c.width = 300,
  cx = cw / 2;
var ch = c.height = 300,
  cy = ch / 2;
var rad = Math.PI / 180;
var R = 100,
  r = 5;

var handle = {
  x: cx + R,
  y: cy,
  r: 5
}
output.style.top = (handle.y - 30) + "px";
output.style.left = (handle.x - 30) + "px";

var isDragging = false;
ctx.strokeStyle = "#555";
ctx.fillStyle = "#e18728";
// circle

drawAxes();
strokeCircle(cx, cy, R);
drawHandle(handle);
drawHub()

// Events ***************************

c.addEventListener("mousedown", function(evt) {
  isDragging = true;
  updateHandle(evt);

}, false);

// mousemove 
c.addEventListener("mousemove", function(evt) {
  if (isDragging) {

    updateHandle(evt);

  }
}, false);
// mouseup 
c.addEventListener("mouseup", function() {
  isDragging = false;
}, false);
// mouseout 
c.addEventListener("mouseout", function(evt) {
  isDragging = false;
}, false); /**/

// Helpers ***************************
function strokeCircle(x, y, r) {
  ctx.beginPath();
  ctx.arc(x, y, r, 0, 2 * Math.PI);
  ctx.stroke();
}

function fillCircle(x, y, r) {
  ctx.beginPath();
  ctx.arc(x, y, r, 0, 2 * Math.PI);
  ctx.save();
  ctx.strokeStyle = "#ccc";
  ctx.lineWidth = 1;
  ctx.fill();
  ctx.stroke();
  ctx.restore()
}

function drawHub() {
  ctx.save();
  ctx.fillStyle = "black";
  fillCircle(cx, cy, 3)
  ctx.restore();
}

function drawAxes() {
  ctx.save();
  ctx.setLineDash([4, 4]);
  ctx.strokeStyle = "#ccc";
  ctx.beginPath();
  ctx.moveTo(cx, 0);
  ctx.lineTo(cx, ch);
  ctx.moveTo(0, cy);
  ctx.lineTo(cw, cy);
  ctx.stroke();
  ctx.restore();
}

function oMousePos(canvas, evt) {
  var rect = canvas.getBoundingClientRect();
  return {
    x: Math.round(evt.clientX - rect.left),
    y: Math.round(evt.clientY - rect.top)
  };
}

function drawHandle(handle) {

  ctx.beginPath();
  ctx.moveTo(cx, cy);
  ctx.lineTo(handle.x, handle.y);
  ctx.stroke();

  fillCircle(handle.x, handle.y, handle.r);

}

function updateHandle(evt) {
  var m = oMousePos(c, evt);
  var deltaX = m.x - cx;
  var deltaY = m.y - cy;
  handle.a = Math.atan2(deltaY, deltaX);
  handle.b = Math.atan(deltaY / deltaX);
  handle.x = cx + R * Math.cos(handle.a);
  handle.y = cy + R * Math.sin(handle.a);
  ctx.clearRect(0, 0, cw, ch);

  drawAxes();
  strokeCircle(cx, cy, R);
  drawHandle(handle);
  drawHub()

  output.innerHTML = parseInt(handle.a * (180 / Math.PI)) + "deg";
  output.style.top = (handle.y - 30) + "px";
  output.style.left = (handle.x - 30) + "px";
  Atan.innerHTML = (handle.b * (180 / Math.PI)).toFixed(2);
  Atan2.innerHTML = (handle.a * (180 / Math.PI)).toFixed(2);
  deltax.innerHTML = deltaX;
  deltay.innerHTML = deltaY;
}

Vea en codepen.io esta aplicación que utiliza Math.atan2() para reorientar los "ojos".