En el ejemplo final de la última sección, vimos cómo podríamos calcular una aceleración dinámica con base en un vector de dirección, desde un círculo en la pantalla a la ubicación del ratón. El movimiento resultante se pareció a la atracción magnética entre el círculo y el ratón, como si alguna fuerza estuviera jalando al círculo hacia el ratón. En esta sección, formalizaremos la comprensión del concepto de una fuerza y su relación con la aceleración. Nuestro objetivo, al final de esto, es entender cómo hacer que múltiples objetos se muevan por la pantalla y respondan a una variedad de fuerzas ambientales.
Antes de comenzar a examinar las realidades prácticas de simular fuerzas en código, echemos un vistazo conceptual sobre lo que significa ser una fuerza en el mundo real. Igual que la palabra “vector”, “fuerza” a menudo se utiliza para decir una variedad de cosas. Puede indicar una intensidad poderosa, como en “Ella empujó la roca con gran fuerza” o “El habló con fuerza”. La definición de la fuerza que nos importa es mucho más formal y proviene de las leyes de movimiento de Isaac Newton:
<div class="callout">
Una fuerza es un vector que provoca que un objeto con masa se acelere.
</div>
La buena noticia es que reconocemos la primera parte de la definición: una fuerza es un vector. ¡Afortunadamente acabamos de pasar toda una sección aprendiendo qué es un vector cómo y programar con PVectors!
Echemos un vistazo a las tres leyes de movimiento de Newton en relación al concepto de una fuerza.

Primera ley de Newton

La primera ley de Newton comúnmente se expresa como:
<div class="callout">
Un objeto en reposo permanece en reposo y un objeto en movimiento permanece en movimiento.
</div>
Sin embargo, a esto le falta un elemento importante relacionado con las fuerzas. Podríamos ampliarla al afirmar:
<div class="callout">
Un objeto en reposo permanece en reposo y un objeto en movimiento permanece en movimiento a velocidad y dirección constantes a menos que sobre él actúe una fuerza externa y no balanceada.
</div>
Para cuando Newton llegó, la teoría predominante del movimiento (formulada por Aristóteles) tenía casi 2 mil años. Expresaba que si un objeto se está moviendo, una especie de fuerza es necesaria para mantenerlo en movimiento. A menos que esa cosa que se está moviendo esté siendo empujada o jalada, simplemente se frenará o se detendrá. ¿Verdad?
Esto, por supuesto, no es cierto. En ausencia de cualquier fuerza, no se requiere ninguna fuerza para que un objeto se siga moviendo. Un objeto (como una pelota) lanzada en la atmósfera terrestre se frena debido a la resistencia del aire (una fuerza). La velocidad de un objeto solo se mantendrá constante en ausencia de cualquier fuerza o si las fuerzas que actúan sobre el objeto se cancelan mutuamente, es decir, la fuerza neta suma cero. Esto a menudo se refiere como equilibrio. La pelota que está cayendo alcanzará una velocidad terminal (que se mantiene constante) una vez que la fuerza de la resistencia del aire iguale la fuerza de gravedad.
Diagrama de dos personas soplando en un péndulo
El péndulo no se mueve porque todas las fuerzas se cancelan mutuamente (suman a una fuerza neta de cero)
En nuestro mundo de ProcessingJS, podríamos replantear la primera ley de Newton como sigue:
<div class="callout">
El PVector de velocidad de un objeto permanecerá constante si está en un estado de equilibrio.
</div>
Omitiendo la segunda ley de Newton (podría decirse que la ley más importante para nuestros propósitos) por un momento, vamos a pasar a la tercera ley.

Tercera ley de Newton

Esta ley a menudo se expresa como:
<div class="callout">
Para cada acción hay una reacción igual y en sentido opuesto.
</div>
Esta ley frecuentemente provoca algo de confusión en la forma en que se expresa. Por un lado, suena como que una fuerza causa otra. Sí, si empujas a alguien, ese alguien puede decidir activamente empujarte de regreso. Pero esta no es la acción y la reacción de la que estamos hablando con la tercera ley de Newton.
Digamos que empujas contra una pared. La pared no decide activamente empujarte de regreso. No hay ninguna fuerza de “origen”. Tu empuje simplemente incluye ambas fuerzas, referidas como un “par de acción/reacción”.
Una mejor manera de expresar la ley puede ser:
<div class="callout">
Las fuerzas siempre ocurren en pares. Las dos fuerzas son de la misma intensidad, pero en direcciones opuestas.
</div>
Ahora, esto todavía causa confusión porque suena a que estas fuerzas siempre se cancelarían entre sí. No es el caso. Recuerda, las fuerzas actúan sobre objetos diferentes. Y solo porque las dos fuerzas sean iguales, no significa que los movimientos sean iguales (o que los objetos dejarán de moverse).
Trata de empujar un camión estacionado. Aunque el camión es mucho más poderoso que tú, a diferencia de uno en movimiento, un camión estacionado nunca te dominará y te enviará volando hacia atrás. La fuerza que ejerces sobre él es igual y opuesta a la fuerza ejercida sobre tus manos. El resultado depende de una variedad de otros factores. Si el camión es un camión pequeño en una pendiente con hielo, probablemente serás capaz de conseguir que se mueva. Por otro lado, si es un camión muy grande en un camino de terracería y empujas suficientemente fuerte (tal vez incluso tomando impulso al principio), podrías lesionarte la mano.
¿Qué pasaría si empujaras un camión mientras estás usando patines?
Un hombre empujando un camión mientras usa patines
Vamos a replantear la tercera ley de Newton para nuestro mundo de ProcessingJS:
<div class="callout">
Si calculamos un PVector f que es una fuerza del objeto A sobre el objeto B, también debemos aplicar la fuerza PVector.mult(f,-1); que B ejerce sobre A.
</div>
Vamos a ver que en el mundo de la programación de ProcessingJS, no siempre tenemos que ser fieles a lo anterior. Algunas veces, como en el caso de la atracción gravitacional entre cuerpos, vamos a querer modelar fuerzas iguales y opuestas. Otras veces, como cuando simplemente decimos que: “Oye, hay algo de viento en el ambiente”, no nos vamos a molestar en modelar la fuerza que un cuerpo ejerce de regreso sobre el aire. De hecho, ¡no estamos modelando el aire en absoluto! Recuerda, simplemente estamos tomando inspiración de la física del mundo natural, no simulando todo con precisión perfecta.

Segunda ley de Newton

Y aquí estamos con la ley más importante para el programador de ProcessingJS.
Esta ley tradicionalmente se expresa como:
<div class="callout">
La fuerza es igual a la masa por la aceleración.
</div>
O:
F, with, vector, on top, equals, M, A, with, vector, on top
¿Por qué esta es la ley más importante para nosotros? Bueno, vamos a escribirla de una forma diferente.
A, with, vector, on top, equals, F, with, vector, on top, slash, M
La aceleración es directamente proporcional a la fuerza e inversamente proporcional a la masa. Esto significa que si dejas que te empujen, mientras más fuerte te empujen, más rápido te moverás (acelerarás). Mientras más grande seas, más lento te moverás.
<div class="callout">
Peso vs. Masa
La masa de un objeto es una medida de la cantidad de materia en el objeto (medida en kilogramos).
El peso, aunque suele confundirse con la masa, técnicamente es la fuerza de gravedad sobre un objeto. A partir de la segunda ley de Newton, podemos calcularlo como la masa por la aceleración de la gravedad (w = m * g). El peso se mide en newtons.
La densidad está definida como la cantidad de masa por unidad de volumen (gramos por centímetro cúbico, por ejemplo).
Ten en cuenta que un objeto que tiene una masa de un kilogramo en la Tierra tendría una masa de un kilogramo en la Luna. Sin embargo, allá pesaría solo un sexto de lo que pesaría aquí.
</div>
Ahora, en el mundo de ProcessingJS, ¿qué es la masa? ¿No estamos tratando con pixeles? Para empezar en un lugar más sencillo, digamos que en nuestro mundo de pixeles todos los objetos tienen una masa igual a 1. F/1 = F. Y así:
A, with, vector, on top, equals, F, with, vector, on top
La aceleración de un objeto es igual a la fuerza. Estas son buenas noticias. Después de todo, vimos en la sección de Vectores que la aceleración era la clave para controlar el movimiento de nuestros objetos en la pantalla. La ubicación es ajustada por la velocidad, y la velocidad por la aceleración. La aceleración fue donde comenzó todo. Ahora aprendemos que la fuerza es verdaderamente donde comienza todo.
Usemos lo que hemos aprendido para construir nuestro objeto Mover, que actualmente cuenta con ubicación, velocidad y aceleración. Ahora nuestro objetivo es ser capaces de añadirle fuerzas a este objeto, tal vez diciendo:
mover.applyForce(wind); //aplicar la fuerza del viento
o:
mover.applyForce(gravity); //aplicar la fuerza de gravedad
donde wind (viento) y gravity (gravedad) son PVectors. Según la segunda ley de Newton, podríamos implementar esta función como sigue:
Mover.prototype.applyForce = function(force) {
    this.acceleration = force;
};

Acumulación de fuerzas

Esto se ve muy bien. Después de todo, aceleración = fuerza es una traducción literal de la segunda ley de Newton (sin masa). Sin embargo, aquí hay un problema bastante grande. Regresemos a lo que estamos intentando lograr: crear un objeto en movimiento en la pantalla que responda al viento y a la gravedad.
mover.applyForce(wind);
mover.applyForce(gravity);
mover.update();
mover.display();
Bien, seamos la computadora por un momento. Primero, llamamos applyForce() con el viento. Y entonces a la aceleración del objeto Mover se le asigna el PVector del viento. Segundo, llamamos applyForce() con la gravedad. Ahora la aceleración del objeto Mover se hace igual al PVector de la gravedad. Tercero, llamamos update(). ¿Qué sucede en update()? La aceleración se le suma a la velocidad.
velocity.add(acceleration);
No vamos a ver ningún error en nuestro programa, pero ¡recórcholis! Tenemos un gran problema. ¿Cuál es el valor de la aceleración cuando se le suma a la velocidad? Es igual a la fuerza de gravedad. ¡El viento ha quedado fuera! Si llamamos applyForce() más de una vez, reemplaza cada llamada anterior. ¿Cómo vamos a manejar más de una fuerza?
La verdad del asunto aquí es que comenzamos con un enunciado simplificado de la segunda ley de Newton. Aquí está una forma más precisa de ponerla:
<div class="callout">La fuerza neta es igual a la masa por la aceleración.</div>
O bien, la aceleración es igual a la suma de todas las fuerzas dividida entre la masa. Esto tiene mucho sentido. Después de todo, como vimos en la primera ley de Newton, si todas las fuerzas suman cero, un objeto experimenta un estado de equilibrio (es decir, sin aceleración). Nuestra implementación de esto es a través de un proceso conocido como acumulación de fuerzas. En realidad es muy simple; lo que tenemos que hacer es sumar todas las fuerzas. En cualquier momento dado, puede haber 1, 2, 6, 12 o 303 fuerzas. Mientras nuestro objeto sepa cómo acumularlas, no importa cuántas fuerzas actúen sobre él.
Vamos a modificar el método applyForce() para sumarle cada nueva fuerza a la aceleración, acumulándolas:
Mover.prototype.applyForce = function(force) {
    this.acceleration.add(force);
};
Ahora, todavía no hemos terminado. La acumulación de fuerzas tiene una pieza más. Como estamos sumando todas las fuerzas en un momento dado, tenemos que asegurarnos de limpiar la aceleración (es decir, hacerla cero) antes de cada momento que update() se llame. Pensemos acerca del viento por un momento. A veces el viento es muy fuerte, a veces es débil, y a veces no hay nada de viento. En un momento dado, podría haber una gran ráfaga de viento, digamos, cuando el usuario deje apretado el ratón.
if (mousePressed) {
  var wind = new PVector(0.5, 0);
  mover.applyForce(wind);
}
Cuando el usuario suelte el ratón, el viento se detendrá, y de acuerdo con la primera ley de Newton, el objeto continuará moviéndose a una velocidad constante. Sin embargo, si nos hubiéramos olvidado de reiniciar la aceleración a cero, la ráfaga de viento seguiría en efecto. Peor aún, se sumaría a sí misma desde el cuadro anterior, ¡ya que estamos acumulando fuerzas! La aceleración, en nuestra simulación, no tiene memoria; simplemente se calcula con base en las fuerzas ambientales presentes en un momento en el tiempo. Esto es diferente de, digamos, la ubicación, que debe recordar dónde estaba el objeto en el cuadro anterior para moverlo correctamente al siguiente.
La forma más fácil de implementar limpiar la aceleración para cada cuadro es multiplicar el PVector por 0 al final de update().
Mover.prototype.update = function() {
    this.velocity.add(this.acceleration);
    this.location.add(this.velocity);
    this.acceleration.mult(0);
};

Lidiar con la masa

Bien. Tenemos una pequeña adición que hacer antes de que terminemos la integración de las fuerzas en nuestra clase Mover y estemos listos para ver ejemplos. Después de todo, la segunda ley de Newton es realmente F, with, vector, on top, equals, M, A, with, vector, on top, no A, with, vector, on top, equals, F, with, vector, on top. La incorporación de la masa es tan fácil como agregarle una propiedad a nuestro objeto, pero tenemos que pasar un poco más de tiempo aquí porque surgirá una pequeña complicación.
<div class="callout">
Unidades de medida
Ya que estamos introduciendo la masa, es importante hacer una breve nota sobre las unidades de medida. En el mundo real, las cosas se miden en unidades específicas. Decimos que dos objetos están a 3 metros de distancia, la bola de béisbol se está moviendo a una velocidad de 90 millas por hora, o esta bola de boliche tiene una masa de 6 kilogramos. Como veremos más adelante en este curso, a veces queremos tomar en cuenta las unidades del mundo real. Sin embargo, en esta sección, vamos a ignorarlas en su mayoría. Nuestras unidades de medida están en pixeles (“Estos dos círculos están separados 100 pixeles”) y cuadros de animación (“Este círculo se está moviendo a una velocidad de 2 pixeles por cuadro”). En el caso de la masa, no hay ninguna unidad de medida que podamos usar. Vamos a tener que inventarnos algo. En este ejemplo, estamos escogiendo arbitrariamente el número 10. No hay ninguna unidad de medida, aunque podrías disfrutar inventar una unidad propia, como “1 moog” o “1 yurkle”. También cabe señalar que, para fines de demostración, juntaremos la masa con los pixeles (al dibujar, digamos, un círculo de radio 10). Esto nos permitirá visualizar la masa de un objeto. En el mundo real, sin embargo, el tamaño definitivamente no indica la masa. Una pequeña bola de metal podría tener una masa mucho mayor que un globo grande debido a su densidad más alta.
</div>
La masa es un escalar (flotante), no un vector, pues solo es un número que describe la cantidad de materia en un objeto. Podríamos ponernos ser elegantes y calcular el área de una figura como su masa, pero es más sencillo empezar diciendo: “Oye, la masa de este objeto es …mmm, no sé …¿qué tal 1?”
var Mover = function() {
    this.mass = 1;
    this.position = new PVector(random(width), random(height));
    this.velocity = new PVector(0, 0);
    this.acceleration = new PVector(0, 0);
};
Esto no es tan maravilloso ya que las cosas solo se volverán interesantes una vez que tengamos objetos con masa variable, pero esto nos ayudará a comenzar. ¿Dónde entra la masa? La usamos mientras le aplicamos la segunda ley de Newton a nuestro objeto.
Mover.prototype.applyForce = function(force) {
  force.div(this.mass);
  this.acceleration.add(force);
};
Una vez más, aunque nuestro código parece bastante razonable, tenemos un problema bastante grave aquí. Considera el siguiente escenario con dos objetos Mover, ambos siendo arrastrados por una fuerza del viento.
var m1 = new Mover();
var m2 = new Mover();

var wind = new PVector(1, 0);

m1.applyForce(wind);
m2.applyForce(wind);
Una vez más, seamos la computadora. El objeto m1 recibe la fuerza del viento, (1,0), la divide entre la masa (10) y se la suma a la aceleración.
| m1 es igual a la fuerza del viento: | (1,0) | | Dividido entre la masa de 10: | (0.1,0) |
Bien. Pasando al objeto m2. También recibe la fuerza del viento: (1,0). Espera. Detente un segundo. ¿Cuál es el valor de la fuerza del viento? Viendo más de cerca, ¡¡la fuerza del viento ahora en realidad es (0.1,0)!! ¿Te acuerdas de este pequeño detalle sobre cómo trabajar con objetos? Cuando le pasas un objeto (en este caso un PVector) a una función, estás pasándole una referencia a ese objeto. ¡No es una copia! Así que si una función realiza un cambio en ese objeto (que, en este caso, lo hace al dividir entre la masa) entonces ¡ese objeto queda cambiado permanentemente! Pero no queremos que m2 reciba una fuerza dividida entre la masa del objeto m1. Queremos que reciba esa fuerza en su estado original: (1,0). Así que debemos protegernos y hacer una copia del PVector f antes de dividirlo entre la masa. Afortunadamente, el objeto PVector tiene un método conveniente para hacer una copia: get(). get() regresa un nuevo objeto PVector con los mismos datos. Y así podemos modificar applyForce() como sigue:
Mover.prototype.applyForce = function(force) {
    var f = force.get();
    f.div(this.mass);
    this.acceleration.add(f);
};
De manera alternativa, podríamos reescribir el método utilizando la versión estática de div(), usando lo que aprendimos acerca de funciones estáticas de la sección anterior:
Mover.prototype.applyForce = function(force) {
  var f = PVector.div(force, this.mass);
  this.acceleration.add(f);
};
Lo importante es encontrar una manera de no afectar el vector original de fuerza, para que pueda ser aplicado a múltiples objetos Mover.

Crear fuerzas

Sabemos qué es una fuerza (un vector), y sabemos cómo aplicarle una fuerza a un objeto (dividirla entre la masa y sumarla al vector de aceleración del objeto). ¿Qué nos falta? Bueno, aún tenemos que averiguar cómo conseguir una fuerza en primer lugar. ¿De dónde vienen las fuerzas?
A lo largo de esta sección, vamos a ver dos métodos para crear fuerzas en nuestro mundo de ProcessingJS:
  • ¡Inventa una fuerza! Después de todo, tú eres el programador, el creador de tu mundo. No hay ninguna razón por la cual no puedas inventar una fuerza y aplicarla.
  • ¡Modela una fuerza! Sí, las fuerzas existen en el mundo real. Y los libros de física a menudo contienen fórmulas para estas fuerzas. Podemos tomar estas fórmulas, traducirlas en código fuente y modelar las fuerzas del mundo real en ProcessingJS.
La forma más fácil para hacer una fuerza es simplemente elegir un número. Vamos a empezar con la idea de simular el viento. ¿Qué tal un viento cuya fuerza apunte a la derecha y sea bastante débil? Suponiendo un objeto Mover m, nuestro código se vería como:
var wind = new PVector(0.01, 0);
  m.applyForce(wind);
El resultado no es muy interesante, pero es un buen lugar para empezar. Creamos un objeto PVector, lo inicializamos y se lo pasamos a un objeto (que a su vez lo aplicará a su propia aceleración). Si hubiéramos querido tener dos fuerzas, tal vez viento y gravedad (un poco más fuerte, apuntando hacia abajo), podríamos haber escrito lo siguiente:
var wind = new PVector(0.01, 0);
var gravity = new PVector(0, 0.1);
m.applyForce(wind);
m.applyForce(gravity);
Ahora tenemos dos fuerzas, que apuntan en diferentes direcciones con distintas magnitudes, ambas aplicadas al objeto m. Estamos empezando a llegar a algo. Hemos construido un mundo para nuestros objetos en ProcessingJS, un ambiente al cual realmente puedan responder.
Aquí está cómo se ve nuestro programa, cuando ponemos todo junto:
¡Vaya! Hemos cubierto mucho, pero ahora podemos hacer mucho. Continúa: ¡aprende a usar la fuerza!

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.