La arcotangente
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 centrocx
y el ratónm.x
:var deltaX = m.x - cx;
- La variable
deltaY
representa la distancia en y entre el centrocy
y el ratónm.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.
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".