Repasando Processing / Redes

En esta carpeta guardo una serie de sketches que facilitan el diseño de redes poligonales regulares.

Repasando Processing / Funciones y clases

En la carpeta Funciones y Clases encontrarás varios ejemplos de sketches que emplean esos elementos. Son aspectos que ya se han tratado aquí y que están comentados en el código, así que no merece la pena extenderse.

Dejo el sketch de la carpeta 4

Repasando Processing / Condicionales y bucles

En apuntes anteriores ya hemos comentado el uso de algunos condicionales. En la carpeta Condicionales y bucles de nuestro repositorio en GitHub encontrarás ejemplosmás o menos detallados de todo. Aquí nos limitaremos a hacer un rápido resumen de este tipo de funciones.

if()

Una función condicional if() podría tener esta forma:

int a = 1;
if(a = 0){point(50, 50);}

Lo que hace es evaluar la condición expresada entre paréntesis y, si es cierta, ejecuta la función indicada entre llaves. En este caso podríamos traducirlo como: “si a es igual a 0, dibuja un punto en las coordenadas 50, 50”. Si la condición no se da, la función no se ejecuta. En este ejemplo el programa mostraría una ventana en blanco, puesto que la condición no se da.

if() / else

Este tipo de condicional sirve para ejecutar una u otra función dependiendo de si una condición se cumple o no:

int a = 1;
if(a = 0){point(50, 50);}
else {ellipse(50, 50, 100, 100);}

“Si a es igual a 0, dibuja un punto, si no lo es dibuja una circunferencia”

else if()

Evalúa una serie de condicionales:

int a = random(10);
if(a < 3){point(50, 50);}
else if(a > 6){ellipse(50, 50, 100, 100);}
else {rect(10, 10, 90, 90);}

“Si a es menor que 3, dibuja un punto; si es mayor que 6, dibuja una circunferencia; si ninguna de las dos condiciones es cierta, dibuja un cuadrado”

if() anidados

Si se da una primera condición, evalúa si se cumple una segunda

int a = random(10);
int b = random(5);
if(a < 6){
  if(b < 2.5){
    point(50, 50);
  } else {ellipse(50, 50, 100, 100);}
}

“Si a es menor que 6 y b es menor que 0.5, dibuja un punto; si a es menor que 6 y b no es menor que 2.5, dibuja una circunferencia; si a es mayor que 6, no hagas nada”

for()

Este bucle tiene una sintaxis especial:

for(int i = 0; i < 50, i++){point(random(width), random(height))}

Lo llamamos bucle y no condicional porque lo que hace es repetir una función mientras la condición se cumpla. La condición en este ejemplo sería: “i es igual a 0; mientras i sea menor que 50, añade uno a i”. Es decir, que la función indicada entre corchetes se repetiría en este caso 50 veces, dibujando cincuenta puntos aleatorios en la ventana.

while()

Es similar a for(), ejecutándose repetidas veces mientras una condición sea cierta

int i = 0;
while(int<50){
  point(random(width), random(height));
  i++;
}

El resultado es el mismo que en el anterior.

?:

Es otro modo de utilizar if()/else al asignar un valor a una variable:

a = (b == 1)? 125 : 255;

“Si el valor de b es 1, el valor de a es 125; si el valor de b no es 1, el valor de a es 255”

switch()

Muy útil si tenemos una serie larga de funciones que queremos que se ejecuten cuando el valor de una variable coincida con el que le indiquemos.

switch(selector){
  case A:
  funcion_1();
  break;
  case B:
  funcion_2();
  break;
  case C:
  funcion_3();
  break;
}

“Si el valor de la variable ‘selector’ es ‘A’, ejecuta funcion_1; si el valor es B, ejecuta funcion_2, etc…”

En todo caso esto es apenas una chuleta, recomiendo consultar los ejemplos de Condicionales y bucles en GitHub, que son más completos

Repasando Processing / pushMatrix(), popMatrix()

En el primer post sobre transformaciones ya expliqué cómo maneja Processing este tipo de operaciones y cómo estas se suman cuando hay varias en un mismo sketch. Y también adelanté que hay formas de resetear las tranformaciones para que resulte más sencillo operar con ellas.

En el siguiente sketch hay dos grupos de tres cuadrados a los que se aplica transformaciones de escalado.

En el grupo de la izquierda cada cuadrado es un 80% menor que el anterior

scale(0.8);

Como las transformaciones se aplican a todo el plano y se suman, el resultado es que cada cuadrado no sólo se reduce sino que cambia de posición, al escalarse también el sistema de coordenadas.

Al final de ese primer bloque hay una función resetMatrix() que devuelve el origen de coordenadas a su posición inicial, por lo que las transformaciones siguientes no se sumarán a las anteriores.

En el segundo bloque de cuadrados vemos que todos tienen la misma posición en el plano, y que están escritos entre funciones pushMatrix() y popMatrix().

//Primer cuadrado
pushMatrix();
translate(width*0.75, height/2);
rect(0, 0, 100, 100);
popMatrix();

//Segundo cuadrado
pushMatrix();
translate(width*0.75, height/2);
scale(0.8);
rect(0, 0, 100, 100);
popMatrix();

//Tercer cuadrado
pushMatrix();
translate(width*0.75, height/2);
scale(0.6);
rect(0, 0, 100, 100);
popMatrix();

pushMatrix() guarda la matriz de coordenadas en un momento determinado del sketch, y popMatrix() las restablece después de haber realizado alguna transformación o transformaciones. De esta manera cada una de ellas no se suma a las anteriores, sino que se efectúan todas desde la misma posición original.

Estos otros ejemplos servirán para comprender mejor el modo como operan estas funciones:

Repasando Processing / Transformaciones / rotate()

En este sketch tenemos tres cuadrados en posiciones diferentes girando alrededor de un centro situado en las coordenadas 0, 0.

Al igual que en en el ejemplo anterior las transformaciones se suman, por eso cada cuadrado describe un ángulo mayor que el anterior.

Repasando Processing / Transformaciones / translate()

Las transformaciones se definen en geometría como los procedimientos que hacen corresponder a cada punto del plano otro punto del plano de forma biunívoca. Básicamente consisten en tomar un elemento de un sitio y ponerlo en otro. Processing implementa varias funciones que permiten efectuar algunas de estas operaciones.

translate() mueve cada elemento una distancia dada en una dirección también dada. Podemos imaginar esa distancia y esa dirección como un vector, de manera que su longitud indica la distancia y su ángulo indica la dirección. Esta función toma dos parámetros, x e y. El vector que parte de las coordenadas 0,0 y acaba en x, y es el que define la traslación.

Sé que estoy dando una definición bastante técnica de una operación que se puede explicar más fácilmente como trasladar un objeto de una posición a otra. Hay un motivo para que lo haga así, y es que si estás familiarizado con las operaciones de traslación, rotación, escala, etc. en entornos de diseño como Illustrator o Photoshop, posiblemente te resulte poco intuitivo el modo como Processing las efectúa.

Primero, es importante tener en cuenta que cuando hacemos una traslación o un giro, no aplicamos estas operaciones a un objeto, sino a todo el plano donde se encuentran esos objetos, incluidos sus ejes de coordenadas.

Por tanto cuando aplicamos la primera transformación

translate(100, 50);

estamos moviendo el origen de coordenadas a esa posición. Por tanto, un cuadrado que tenga como coordenadas 0,0, después de la transformación se dibujará en 100, 50. En el siguiente dibujo, ese vector se muestra en color amarillo.

translate()

La segunda transformación, representada por el vector cian, es:

translate(100, 0);

Es decir, mover 100 píxeles a la derecha. Pero como esa transformación se suma a la anterior, el resultado es un cuadrado situado a 200 píxeles del borde izquierdo (100 + 100) y a 50 del borde superior (50 + 0). Sin embargo, si observamos el código vemos que las coordenadas del segundo rectángulo son 0, 0. ¿Qué está pasando? Sencillamente que las dos transformaciones anteriores han situado el origen de coordenadas (0, 0), en la posición 200, 50 tomada desde la esquina superior izquierda.

Lo mismo pasa con la tercera transformación, indicada en magenta, que vuelve a mover el plano 100px a la derecha.

Todo esto puede parecer poco intuitivo, y lo es, pero una vez comprendida la lógica detrás de estas operaciones al menos no será un laberinto. Además, como veremos más adelante, es posible resetear las coordenadas después de una transformación para que estas no se sumen.