Los bucles do while difieren ligeramente de los bucles while. Si observamos el ejemplo, hay un cambio en la estructura y lo que los diferencia es que lo que contenga nuestro bucle do while en su interior se ejecutará al menos una vez.
void setup(){ size(500, 500);
int y = 10; do{ rect(10, y, 480, 5); y = y + 10; }while(y < 490); }
Estos bucles tienen utilidad cuando necesitamos tener la certeza de que lo que contiene nuestro bucle en su interior se va a ejecutar al menos una vez.
Los bucles forse utilizan para compactar largas líneas de código repetitivo. Disminuyen la longitud del código, pueden hacer que los programas sean más fáciles de manejar y ayudan a reducir los errores. REPETICIÓN
El flujo de una estructura puede ser mostrado como un diagrama. La imagen superior muestra la importancia central de la declaración de prueba (test) para decidir si se debe ejecutar el código del bloque (statements) o para salir de él.
Como ya hemos comentado, un bucle for realiza cálculos repetitivos y tiene una estructura como esta:
for(init, test, update){ statements
}
En el paréntesis asociado con la estructura se encierran tres declaraciones:
inicio (init), prueba (test) y actualización (update).
Las declaraciones (statements) dentro del bloque se ejecutan continuamente mientras la prueba se evalúa como verdadera.
init: asigna el valor inicial de la variable utilizada en la prueba.
test: evalúa si es verdadero (true) o falso (false).
update: la actualización se utiliza para modificar la variable después de cada iteración a través del bloque.
Una estructura for se ejecuta siguiendo la siguiente secuencia:
La declaración init está corriendo.
test es evaluado a true o false.
Si test es true, continue al paso 4. Si test es false, salta al paso 6.
Ejecuta las declaraciones en el bloque de código.
Ejecuta la declaración update y salta al paso 2.
Salir de la estructura y continuar corriendo el programa.
Los siguientes ejemplos demuestran como una estructura for es usada dentro de un programa para controlar las formas que son dibujadas.
_________________________________________________
La estructura for produce repeticiones en una sola dirección. La anidación de una de estas estructuras en otra, crea una iteración en dos dimensiones.
Código 4.10-1
size(600, 600); strokeWeight(5); for(int y = 20; y < 600; y = y+20){ point(10, y); }
__________________________________________
Código 4.10-2
size(600, 600); strokeWeight(5); for(int x = 20; x < 600; x = x+20){ point(10, y); }
__________________________________________
Código 4.10-3
size(600, 600); strokeWeight(5); for(int y = 20; y < 600; y = y+20){ for(int x = 20; x < 600; x = x+20){ point(10, y); } }
__________________________________________
Esta técnica es útil para crear diversos patrones y efectos. Los números producidos mediante la incorporación de elementos iterativos se pueden aplicar al color, la posición, el tamaño, la transparencia y a cualquier otro atributo visual.
Código 4.10-4
size(480, 480); background(0);
noStroke();
for(int y = 0; y <= height; y = y+30){ for(int x = 0; x <= width; x = x+30){ fill(255, 70); ellipse(x, y, 40, 40); } }
Hasta el momento, cada vez que hemos utilizado variables, las hemos declarado en la parte superior de nuestro programa anterior a setup().
Esta es una buena simplificación y nos ha permitido centrarnos en los fundamentos de la Declaración, inicialización y uso de variables. Las variables, sin embargo, se pueden declarar en cualquier lugar dentro de un programa y ahora se verá en lo que significa declarar una variable de algún lugar que no sea la parte superior y cómo podemos elegir la ubicación correcta para declarar una variable.
Imaginemos, por un momento que un programa de ordenador está funcionando en nuestra vida. Y en esta vida, las variables son post-it que necesitamos recordar.
Un post-it podría tener la dirección de un restaurante para el almuerzo. Lo escribes esta mañana y lo tiras a la basura después de disfrutar de una buena hamburguesa de pavo.
Pero otro post-it puede contener información crucial (como número de cuenta bancaria), y debe estar guardado en un lugar seguro durante años y años. Este es el concepto del alcance.
Existen algunas variables (es decir, que se pueden acceder a ellas) a lo largo de todo el curso de vida global de un programa y otras solo sirven temporalmente, sólo para el breve momento en que se requiere un valor para una instrucción o variables de cálculo local.
En Processing, las variables globales se declaran en la parte superior del programa, tanto fuera de setup() y draw(). Estas variables pueden ser usadas en cualquier parte del programa. Es la forma más fácil de usar una variable, ya que no tiene que recordar cuando se puede y no se puede utilizar esa variable. Tu puedes usar siempre estas variables (globales) y es por eso que empezamos solo con variables globales.
Las variables locales son variables declaradas dentro de un bloque de código. Hasta ahora hemos conocido muchos ejemplos diferentes de bloques de código: setup(), draw(), mousePressed(), keyPressed(), las declaraciones if, también los bucles while y for.
Una variable local declarada en un bloque de código sólo está disponible para su uso dentro de ese bloque de código específico en la que se declaró. Si intentas acceder a una variable local fuera del bloque en el que se declaró, obtendrás este error:
“No accessible field named “”variebleName” was found”
Este es el mismo error que se obtendría si no se molestó en declarar la variable “nombre de Variable” en tu programa. Processing no sabe lo que es porque no hay ninguna variable con ese nombre que exista dentro del bloque de código donde debería estar.
Aquí está un ejemplo donde una variable local se utiliza dentro de draw() con el fin de ejecutar un bucle while().
Example 6-7: Local variable
void setup(){
size(200, 200);
}
void draw(){
background(0);
int x = 0;
while(x < width){
stroke(255);
line(x, 0, x, height);
x += 5;
}
}
void mousePressed(){
println(“The mouse was pressed!”);
}
¿Por qué molestarse? ¿No podríamos sencillamente declarar x como una variable global? Si bien esto es cierto, ya que solo estamos usando x dentro de la función draw(), es un desperdicio tener todas nuestras variables como variables globales.
Es más eficiente y en última instancia menos confuso cuando se programa que la declaración de variables se realice dentro del alcance de donde sean necesarias.
Ciertamente, muchas variables deben ser globales, pero este no es el caso aquí.
Un bucle for ofrece un punto más cercano para que podamos entender una variable “local” dentro de la parte de “inicialización”.
for(int i = 0; i < 100; i+=10){
stroke(255);
fill(i);
rect(i, 0, 10, height);
}
No es necesario utilizar una variable local en el bucle, sin embargo, por lo general es conveniente hacerlo.
En teoría, es posible declarar una variable local con el mismo nombre que una variable global. En este caso, el programa utilizará la variable local dentro del ámbito actual y la variable global fuera de ese ámbito actual y la variable global fuera de ese ámbito. Como regla general, es mejor no declarar múltiples variables con el mismo nombre con el fin de evitar confusiones.
Las ecuaciones matemáticas básicas pueden utilizarse para dibujar formas a la pantalla y modificar sus atributos. Estas ecuaciones aumentan las funciones de dibujo discutidos hasta ahora.
Nos permite controlar el movimiento y la forma en que los elementos responder al cursor. Estas matemáticas se utilizan para acelerar y desacelerar formas en movimientos y mover los objetos a lo largo de trayectorias curvas.
Exponentes y Raíces
La función sq() se utiliza para elevar al cuadrado un número y devolver el resultado. El resultado es siempre un número positivo, porque la multiplicación de dos números negativos incluso da un resultado positivo. Por ejemplo, -1 * -1 = 1.
Esta función tiene un parámetro:
sq(value)
Cuando se utiliza sq(), el resultado se puede asignar a una variable:
float a = sq(1); // Asigna 1 a “a”: Equivale a 1*1
float b = sq(-5); // Asigna 25 a “b”: Equivale a -5 * -5
float c = sq(9); // Asigna 81 a “c”: Equivale a 9 * 9
La función sqrt() se utiliza para calcular la raíz cuadrada de un número y devolver el resultado. Es lo opuesto de sq().
La raíz cuadrada de un número es siempre positivo, a pesar de que puede haber una raíz cuadrada del número satisface la ecuación s * s = a.
Esta función tiene un parámetro que debe ser un número positivo:
sqrt(value)
Como en la función de sq(), el parámetro de valor puede ser cualquier número, y cuando la función se utiliza el resultado puede ser asignado a una variable:
float a = sqrt(6561); // Asigna 81 a “a”
float b = sqrt(625); // Asigna 25 “b”
float c = sqrt(1); // Asigna 1 a “c”
La función pow() calcula un número elevado a un exponente. Tiene dos parámetros:
pow(num, exponente)
El parámetro número es el número a multiplicar, y el parámetro exponente es el número de veces para hacer el cálculo. El siguiente ejemplo muestra cómo se utiliza:
float a = pow(1, 3); // Asigna 1.0 a “a”: Equivale a 1*1*1
float b = pow(3, 4); // Asigna 81.0 a “b”: Equivalente a 3*3*3
float c = pow(3, -2); // Asigna 0.11 a “c”: Equivalente 1/3*3
float d = pow(-3, 3); // Asigna -27.0 a “d”: Equivale a -3*-3*-3*-3
Cualquier número (excepto 0) elevado a la potencia cero es igual a 1. Cualquier número elevado elevado a la potencia de uno es igual a sí mismo.
float a = pow(8, 0); // Asigna 1 a “a”
float b = pow(3, 1); // Asigna 3 a “b”
float c = pow(4, 1); // Asigna 4 a “c”
Normalización, Mapeo
Los números se convierten a menudo a el rango de 0.0 a 1.0 para realizar los cálculos. Esto se conoce como la normalización de los valores.
Cuando los números entre 0.0 y 1.0 se multiplican entre sí, el resultado nunca es inferior a 0.0 o superior 1.0.
Esto permite que cualquier número que se multiplica por otro o por sí mismo muchas veces sin salir a esta gama.
Por ejemplo, multiplicando el valor 0.2 por sí mismo 5 veces (0.2 * 0.2 * 0.2 * 0.2) produce el resultado 0.00032.
Debido a que los números normalizados tienen un punto decimal, todos los cálculos se deben hacer con el tipo de datos float.
Para normalizar un número, se divide por el valor máximo que representa. Por ejemplo, para normalizar una serie de valores entre 0.0 y 255.0. 255.0 dividir cada por:
Esto también se puede lograr a través de la función norm(). Tiene tres parametros:
norm(value, low, hight)
El número utilizado como parámetro de valor se convierte a un valor entre 0.0 y 1.0
Los parámetros de bajas y altas establecen los respectivos valores mínimos y máximos de los números de rango actual de número.
Si el valor está fuera del rango, el resultado puede ser menor que 0 o mayor que 1.
El siguiente ejemplo muestra cómo utilizar la función de hacer los mismos cálculos que la tabla anterior.
float x = norm(0.0, 0.0, 255.0); // Asigna 0.0 a x
float y = norm(102.0, 0.0, 255.0); // Asigna 0.4 a y
float z = norm(255.0, 0.0, 255.0); // Assign 1.0 a z
Después de la normalización, un número se puede convertir en otro rango a través de la aritmética.
Para convertir números entre 0.0 y 1.0 en un rango entre 0.0 y 500.0, multiplica por 500.0.
Para poner los números de entre 0.0 y 1.0 para los números entre 200.0 y 250.0, se multiplica por 50 y luego añadir 200. La siguiente tabla presenta algunas conversiones de muestra:
Los paréntesis se utilizan para mejorar la legibilidad.
La función lerp() se puede utilizar para llevar a cabo estos cálculos. El nombre “lerp” es una contracción de la función “interpolación lineal” y tiene tres parámetros.
lerp(valor1, valor2, amt)
Los parteros valor1 y valor2 definen los valores mínimo y máximo, y el parámetro amt siempre lee un valor entre 0.0 y 1.0. El siguiente ejemplo muestra cómo utilizar lerp() para realizar las conversiones de valor en la última línea de la tabla anterior.
float x = lerp(-20, 60, 0.0); // Asigna -20.0 a x
float y = lerp(-20, 60, 0.5); // Asigna 20.0 a y
float z = lerp(-20, 60.0, 1.0); // Asigna 60.0 a z
La función map() es útil para convertir directamente de un rango de números a otro. Cuenta con cinco parámetros.
map(value, low, hight1, low2, hight2)
El parametro valor es el número de re-map. Similar a la función norma, los parámetros low1 y hight1 son los valores mínimo y máximo del rango actual del número.
Los parámetros low2 y hight2 son los valores mínimos y máximos de la nueva gama.
El siguiente ejemplo muestra cómo utilizar el map() para convertir los valores de la gama de 0 a 255 en el rango de -1 a 1. Este es el mismo que el primero normaliza el valor, a continuación, multiplicando y sumando para moverlo de la gama de 0 a 1 en el rango de -1 a 1.
float x = map(20.0, 0.0, 255.0, -1.0, 1.0); // Asignar -84 a “x”
float y = map(0.0, 0.0, 255.0, -1.0, 1.0); // Asignar -1.0 a “y”
float z = map(255.0, 0.0, 255.0, -1.0, 1.0); // Asignar 1.0 a “z”
Curvas simples
Las funciones exponenciales son útiles para crear curvas simples. Los valores normalizados se utilizan con la función pow() para producir de manera exponencial aumento a disminución de los números que nunca exceder el valor 1.
Estas ecuaciones tienen la forma:
y = x2
Donde el valor de x está entre 0.0 y 1.0 y el valor de n es cualquier número entero. En estas ecuaciones, como el valor de x aumenta linealmente los resultados aumentan de valor y de manera exponencial.
El siguiente ejemplo muestra cómo poner esta ecuación en código. se itera sobre los números de 0 a 100 y normaliza los valores antes de hacer el cálculo de la curva.
for(int x = 0; x < 100; x++){
float n = norm(x, 0.0, 100.0); // Rango de 0.0 a 1.0
float y = pow(n, 4); // Calcular curva
y *= 100;
point(x, y);
}
Otras curvas puedes crear modificando los parteros de pow() en la línea 3.
for(int x = 0; x < 100; x++){
float n = norm(x, 0.0, 100.0);
float y = pow(n, 0.4);
y *= 100;
point(x, y);
}
Los siguientes tres ejemplos demuestran cómo la misma curva es usada para dibujar diferentes formas y patrones.
Dibuja círculos en los puntos a lo largo de la curva y = x^4
noFill();
for(int x = 0; x < 100; x+=5){
float n = norm(x, 0.0, 100.0); // Rango de 0.0 a 1.0
El tipo de datos de color se utiliza para almacenar colores en un programa, y la función color() se utiliza para asignar una variable de color.
La función color() se utiliza de la siguiente manera y puede almacenar valores de gris, gris con transparencia, valores de color y valores de color con transparencia.
Los parámetros de la función color() definen un color. Esta función nos permite guardar colores como si fuesen variables y se asignan del mismo modo que fill() o stroke().
color c = color(51); color c2 = color(51, 204); color c3 = color(51, 102, 153); color c4 = color(51, 102, 153, 51);
Después de definir una variable de color, puede ser utilizada como parámetro para las funciones background(), fill() y stroke().
color ruby = color(211, 24, 24, 160); color pink = color(237, 159, 176); background(pink); noStroke(); fill(ruby); rect(35, 0, 20, 100);
Nuestro código puede responder a inputs desde el mouse, teclado y otros dispositivos que estén corriendo continuamente. Para que nuestro código sea interactivo y tenga movimiento vamos a presentar dos importantes funciones.
void setup(){ }
void draw(){ }
Normalmente, todos nuestros códigos van a contener estas dos funciones.
void setup() se configura y se lee una sola vez. Suele servir para introducir funciones y parámetros con los que iniciaremos nuestro programa y código que no varía. Por ejemplo, en void setup() es donde coloca la función de tamaño size(widht, height) para nuestra aplicación. Dado que sus parámetros no serán modificados mientras reproducimos nuestro programa.
void draw() es leído continuamente, a 60 frames por segundos, si no le decimos lo contrario a Processing y nuestro equipo tiene la suficiente potencia. Aquí es donde processing crea las animaciones y la interacción. Todo lo que metamos entre los dos corchetes ({}) de void draw() será leído repetidamente hasta que detengamos nuestro programa.
Al llegar al final del código de void draw() vuelve de nuevo al principio y repite el proceso indefinidamente hasta que pulsamos STOP.
Los corchetes ({…}) delimitan cada parte, son conocidos como bloques de código. Es algo así como los párrafos cuando escribimos en un lenguaje hablado. Sirven para organizar mejor nuestro código, separarlo visualmente y también nos permiten introducir otro concepto importante en programación: El alcance de las variables, que explicamos en la siguiente tema.
Es importante que utilices bien los espacios y la tabulación cuando escribas tus programas con el fin de poder visualizar claramente los bloques y las partes de tu código. Intenta ser organizado y limpio cuando escribas tus programas, no te olvides de utilizar comentarios aclaratorios con la doble barra (//) o con barra asterisco, comentario, asterisco barra (/* … */) para comentarios de varias líneas.
Otro aspecto que tienes que tener en cuenta es que Processing es Case sensitive. Esto significa que diferencia entre mayúsculas y minúsculas. Así que escribe los códigos tal y como se muestran en el blog o tus programas no funcionarán.
Como ya hemos comentado en el capítulo 4, Processing genera una serie de variables automáticamente cuando un programa está corriendo. Dos de estas variables son mouseX y mouseY y determinan la posición del ratón en el display window.
mouseX = posición del ratón en X
mouseY = posición del ratón en Y
Estas devuelven un valor en pixeles que puede ser leído y utilizado para modificar otros valores o para interactuar con los elementos de la ventana.
Además de mouseX y mouseY, Processing nos proporciona otras dos variables para el ratón, que son pmouseX y pmouseY, que indica la posición del ratón en el frame anterior.
Ya hemos comentado, que esto sucede muy rápido, a 60 frames por segundo, significa que estas variables cambiarán su valor 60 veces en un solo segundo, lo que dará una sensación de continuidad y movimiento.
Al tener dos valores diferentes, podemos utilizarlos para calcular la velocidad a la que se mueve el ratón por el display window, y estos valores se estarán calculando constantemente en tiempo real.
Ahora veremos un ejemplo.
Observando la imagen superior, por ejemplo, si nos encontramos en el frame 99, vemos que el valor pmouseX y pmouseY es igual a 20, lo que significa que el ratón esta en la posición 20, 20. En el frame actual, el número 100 el ratón se encuentra en la posición 50, 50. Y cuando nos encontremos en el 101 las variables mouseX y mouseY ya serán otras nuevas y pmouseX y pmouseY tendrán un valor de 50, 50.
Esto sucede tan rápido, que es difícil intentar explicarlo y detenerlo en el tiempo. Lo mejor es que veamos un ejemplo práctico para demostrar cómo funcionan estas dos variables.