3. Reto 3. Interacciones

3.2. Las funciones draw() y setup()

Ya habíamos comentado en retos anteriores que el código que ponemos a la función setup() solo se ejecuta una vez, mientras que el que ponemos a la función draw() se ejecuta continuamente. Como explica el libro, la función draw() se ejecuta 60 veces por segundo (más adelante veremos que podemos modificar esta velocidad) y para demostrarlo nos propone ejecutar el código del ejemplo 5-1:

function draw() {
  // Displays the frame count to the console
  print("I’m drawing");
  print(frameCount);
}

Sí, sin nada más, sin función setup().

Este programa escribirá a la consola JavaScript la frase «I’m drawing» y el número de fotogramas que se han ejecutado. La variable frameCount guarda el número de fotogramas que se han visualizado desde el comienzo de la ejecución del código. Como hemos visto, por defecto se visualizan 60 fotogramas por segundo.

En esta captura de pantalla podéis ver cómo se va escribiendo el texto en la consola cada vez que se ejecuta la función draw().

El ejemplo 5-2 propone una cosa parecida, pero usando la función setup() y la función draw() para comparar los resultados. Seguro que sin ejecutar el programa ya os hacéis una idea de qué es lo que pasará.

Al final de este ejemplo nos explica los tres lugares donde podemos poner código en un programa p5.js:

  1. Fuera de setup() y draw() pondremos las declaraciones de variables.
  2. Dentro de setup() pondremos el código que solo se tenga que ejecutar una vez.
  3. Dentro de draw() pondremos el código que se ejecuta continuamente.

Como el resto de ejemplos del capítulo 5 son bastante claros, os recomendamos que los hagáis todos. A continuación, os hacemos unas breves aclaraciones de puntos que pensamos pueden generar alguna duda.

Para probar estos ejemplos, podéis usar el editor de p5.js.

La posición del ratón

Los ejemplos 5-4 y 5-5 son muy parecidos. En los dos casos en la función setup() creamos el canvas, definimos el color (negro) y la transparencia (102 sobre 255) con la instrucción fill() y decimos que no querremos bordes con la instrucción noStroke(). La única diferencia entre los dos ejemplos es el llamamiento a la función background() al comienzo de la función draw(). Esta función vuelve a pintar todo el fondo del color indicado, borrando lo que hubiera antes.

En los dos ejemplos en la función draw() solo tenemos una instrucción:

ellipse(mouseX, mouseY, 9, 9);

¿Qué significa esto? ¿Qué son las variables mouseX mouseY?

Bien, es fácil suponer que estas dos variables nos dicen cuál es la posición del ratón en el momento actual. Así, el código del ejemplo dibuja un círculo allí donde está el ratón. Como la función draw() se repite sesenta veces por segundo, se dibujan 60 círculos cada segundo. Y, así, si movemos el ratón poco a poco, se dibujará una línea bastante negra y, en cambio, si lo movemos rápido podemos, incluso, dejar pequeños espacios entre círculos.

En el ejemplo 5-4 todo lo que se va dibujando se queda en el canvas, mientras que en el ejemplo 5-5, cada vez se pinta el fondo justo antes de dibujar el círculo. De este modo, da la sensación de que el círculo se mueve, a pesar de que lo que estamos haciendo es dibujarlo de nuevo cada vez.

En el ejemplo 5-6 se introducen las variables pmouseX y pmouseY. Estas variables nos dicen cuál era la posición del ratón en el fotograma anterior. Así, podemos pintar una línea continua en vez de solo puntos separados, como hacíamos en el ejemplo anterior. Esta es la instrucción clave:

line(mouseX, mouseY, pmouseX, pmouseY);

Dibujamos una línea desde la posición actual del ratón mouseX, mouseY, a la posición anterior del ratón pmouseX, pmouseY. ¿Podemos dibujar la línea a la inversa (de la posición anterior a la actual)? ¿Habría alguna diferencia? Probadlo.

El ejemplo 5-7 es fácil de entender, aunque incluye dos funciones nuevas: dist() y strokeWeight(). A pesar de que con el contexto se puede saber bien lo que hacen, recordad que en el manual de referencia de p5.js se puede encontrar completa información de estas instrucciones.

El ejemplo 5-8 es más complejo no solo por lo que hace, también por las instrucciones que usa. Recordad que la instrucción x += y es equivalente a x = x + y.

Lo que pretende este ejemplo es mostrar una manera de hacer que el cursor siga al ratón lentamente en vez de instantáneamente, como hace en los ejemplos que hemos visto hasta ahora. De hecho, tal y como está hecho, cuanto más cerca está el ratón, más lentamente se acerca el dibujo a él.

Revisemos este ejemplo:

var x = 0;         // Es la posición x donde dibujaremos el círculo
var easing = 0.01; // Indica cuándo se incrementa la velocidad 
                   // según cuán lejos esté el ratón

function setup() {
  createCanvas(220, 120);
}

function draw() {
  var targetX = mouseX;        // Guardamos la posición x del ratón
  x += (targetX - x) * easing; // Esta es la operación clave
                              // lo explicamos bajo este código

  ellipse(x, 40, 12, 12);
  print(targetX + " : " + x); // Escribe en la consola la posición
                              // x del ratón y la x donde dibujamos
                             // el círculo
}

Empezamos dibujando el círculo en la posición x=0. Dentro de la función draw() guardamos la posición x del ratón y, para decidir dónde dibujaremos el próximo círculo, lo que hacemos es calcular la diferencia de posición entre el ratón (targetX) y el último círculo que hemos dibujado (x) y multiplicarla por esta variable easing que hará que la distancia sea mayor cuanto mayor sea el valor de esta variable.

Probad el ejemplo y probad a cambiar el valor de la variable easing. Mirad los valores que se escriben en la consola. Jugad también con el tamaño del canvas. Quizás, si lo hacemos más largo podemos ver mejor el efecto (¿cuál de los dos valores tenemos que cambiar para cambiar la anchura del canvas?).

En el ejemplo siguiente, 5-9, a más velocidad, mayor es el círculo que se dibuja.

La idea es la misma que la del ejercicio anterior e, incluso, puede resultar más clara. Pero el código es algo más complicado. ¿Qué hacen tantas variables?

Las variables x e y guardan el valor de hasta donde toca dibujar la línea.

Las variables px y py guardan el valor de la posición anterior donde se dibujó (si os fijáis guardan los valores de x e y justo antes de acabar la función draw().

Las variables targetX y targetY se usan para guardar la posición del ratón en un momento dado. En realidad, el uso de las variables targetX y targetY no es imprescindible y podemos hacer lo mismo con este código algo más simple:

var x = 0;
var y = 0;
var px = 0;
var py = 0;
var easing = 0.05;

function setup() {
      createCanvas(480, 480);
      stroke(0, 102);
}
function draw() {
     x += (mouseX - x) * easing;
     y += (mouseY - y) * easing;
     var weight = dist(x, y, px, py);
     strokeWeight(weight);
     line(x, y, px, py);
     py = y;
     px = x;
}

Ahora, cambiad el valor que asignamos a la variable easing, poniendo 0.5 en vez de 0.05, y tratad de responder a esta pregunta:

¿Qué dos cambios se producen cuando modificamos la variable easing?