Regresemos a uno de nuestros primeros ejemplos, donde un objeto Mover se acelera hacia el ratón.
Podrás notar que casi todas las figuras que hemos estado dibujando hasta ahora son círculos. Esto es conveniente por varias razones, una de las cuales es que no tenemos que considerar el asunto de la rotación. Rota un círculo y, bueno, se ve exactamente igual. Sin embargo, llega un momento en la vida de todos los programadores de movimiento cuando quieren dibujar algo en la pantalla que apunta en la dirección del movimiento. Tal vez estás dibujando una hormiga, un coche o una nave espacial. Y cuando decimos "que apunta en la dirección del movimiento", lo que estamos diciendo en realidad es “rotar de acuerdo con el vector de velocidad”. La velocidad es un vector, con una componente x y una y, pero para rotar en ProcessingJS necesitamos un ángulo. Vamos a dibujar nuestro diagrama de trigonometría una vez más, con el vector de velocidad de un objeto:
Bien. Sabemos que la definición de la tangente es:
El problema con lo anterior es que conocemos la velocidad, pero no conocemos el ángulo. Tenemos que despejar el ángulo. Aquí es donde una función especial conocida como tangente inversa entra en juego, a veces se denomina arco tangente o tan-1. (También hay un seno inverso y un coseno inverso).
Si la tangente de algún valor a equivale a un valor b, entonces la tangente inversa de b es igual al valor a. Por ejemplo:
| si | tangente(a) = b | | entonces | a = arco tangente(b) |
¿Ves cómo esa es la inversa? Lo anterior ahora nos permite resolver para el ángulo:
| si | tangente(ángulo) = velocidad_y / velocidad_x | | entonces | ángulo = arco tangente(velocidad_y / velocidad_x) |
Ahora que tenemos la fórmula, vamos a ver dónde debería ir en la función display() de nuestro mover. Observa que en ProcessingJS, la función arco tangente se llama atan(). JavaScript también proporciona Math.atan() nativamente (como todas las funciones trigonométricas básicas), pero nos quedaremos con las funciones que proporciona ProcessingJS.
Mover.prototype.display = function () {
  var angle = atan(this.velocity.y / this.velocity.x);

  stroke(0, 0, 0);
  fill(127, 127, 127);
  pushMatrix();
  rectMode(CENTER);
  translate(this.position.x, this.position.y);
  rotate(angle);
  rect(0, 0, 30, 10);
  popMatrix();
};
El código anterior ya está bastante cerca y casi funciona. Pero todavía tenemos un gran problema. Consideremos los dos vectores de velocidad representados a continuación.
Aunque superficialmente son similares, los dos vectores apuntan en direcciones muy diferentes: ¡direcciones opuestas, de hecho! Sin embargo, si le aplicamos nuestra fórmula para despejar el ángulo a cada vector…
V1 ⇒ angle = atan(-4/3) = atan(-1.333...) = -0.9272952 radianes = -53 grados
V2 ⇒ angle = atan(4/-3) = atan(-1.333...) = -0.9272952 radianes = -53 grados
… obtenemos el mismo ángulo para cada vector. Esto no puede ser correcto para ambos; ¡los vectores apuntan en direcciones opuestas! La cosa es, este es un problema bastante común en gráficas de computadora. En lugar de simplemente utilizar atan() junto con un montón de declaraciones condicionales para tener en cuenta los escenarios positivos/negativos, ProcessingJS (junto con JavaScript y prácticamente todos los entornos de programación) tiene una bonita función llamada atan2() que lo hace por ti.
Mover.prototype.display = function () {
  var angle = atan2(this.velocity.y, this.velocity.x);

  stroke(0, 0, 0);
  fill(127, 127, 127);
  pushMatrix();
  rectMode(CENTER);
  translate(this.position.x, this.position.y);
  rotate(angle);
  rect(0, 0, 30, 10);
  popMatrix();
};
Para simplificar esto aún más, el propio objeto PVector proporciona una función llamada heading(), que se encarga de llamar a atan2() por ti para que puedas obtener el ángulo de dirección 2D, en radianes, para cualquier PVector.
Aquí está cómo se ve el programa, todo junto. ¡Mueve el ratón sobre él y ve cómo gira!

Este curso de "Simulaciones Naturales" es un derivado de "La Naturaleza del Código" por Daniel Shiffman, usado bajo una Licencia Creative Commons Reconocimiento-NoComercial 3.0 Unported.