tutorial de programación para arte interactivo
Emiliano Causa
e_causa@yahoo.com.ar
www.emiliano-causa.com.ar
www.biopus.com.ar
Christian Silva
entorno3@gmail.com
www.hipertextos.com.ar
Leonardo Garay
todotresde@gmail.com
David Bedoian
bedoiandavid@yahoo.com
www.bedoiandavid.com.ar
tutorial de programación para arte interactivo

Arreglos

zip descargar

Introducción

Como vimos en los apartados anteriores, las estructuras iterativas permiten repetir un proceso una gran cantidad de veces, en contraste, cuando se desea realizar un tratamiento homogéneo sobre una gran cantidad de información, las variables, como se han visto hasta aquí, son insuficientes. Esto es debido a que cuandosi desea repetir un procesos sobre una gran cantidad de variables es necesario recurrir a estas haciendo referencia a diferentes nombres, lo que imposibilita utilizar una estructura repetitiva. Por ejemplo, el siguiente es un programa para realizar un promedio de un alumno:

 

float calificacion1 = 9.5;
float calificacion2 = 7;
float calificacion3 = 5;
float total = 0;
int cantidad = 3;
total += calificacion1;
total += calificacion2;
total += calificacion3;
float promedio = total/cantidad;

Hecho en Processing

Si la cantidad de calificaciones a ser promediadas fuesen muchas (por ejemplo 500) el anterior procedimiento no sería aplicable. En una caso como este sería deseable contar con algún tipo de variable que a partir de un único identificador (nombre de la variable) permitiese acceder a gran cantidad de información para der el mismo tratamiento a toda esta información.

ir arriba

Los arreglos

Los arreglos (ver definición en wikipedia) son variables que permiten acceder a una gran cantidad a partir de una único identificador y un índice. Los arreglos pueden ser de varias dimensiones en función de la cantidad de índices que utilizan. Los arreglos de una dimensión son llamados vectores, los de dos dimensiones se llaman matrices, los de tres o más dimensiones se llaman tensores. Los arreglos además de ser inicializados, deben ser dimensionados, asignándoles una extensión, es decir la cantidad de celdas de memoria que utilizará:

 

float[] calificacion; //inicializacion del arreglo
calificacion = new float[100]; //dimensión del arreglo

calificacion[0] = 9.5; //asignacion de la primer celda
calificacion[1] = 7.0; //asignacion de la segunda celda
...

Hecho en Processing

También es posible dimensionar y asignar datos en un mismo paso, como se muestra en el siguiente ejemplo:

 

// inicializacion, dimensión
// y asignaciones del arreglo
float[] calificacion = { 9.5 , 7 , 6.5 , 8 , 10 };
float total = 0;
int cantidad = calificacion.length();
for( int i=0 ; i<cantidad ; i++ ){

    total += calificacion[ i ];
}
float promedio = total/cantidad;
println( "El promedio es " + promedio );

Hecho en Processing

En la tercer línea del ejemplo anterior, se declara el arreglo al mismo tiempo que se le asignan 5 datos ( 9.5 , 7 , 6.5 , 8 y 10 ) lo que determina la extensión de este arreglo ( 5 ). La función length (en la quinta línea) retorna la cantidad de celdas que tiene el arreglo, en este caso, obviamente 5. Las celdas en el arreglo se numeran desde el índice 0 (cero) hasta la cantidad menos uno (4 en este caso), que son justamente los valores que adquiere la variable i en el ciclo for.

ir arriba

Un ejemplo interactivo

El ejemplo que sigue permite generar círculos con el mouse, haciendo click en el fondo se genera un nuevo círculo, y cuando se hace click sobre un círculo ya creado, se le modifica el color y se lo arrastra.

To view this content, you need to install Java from java.com

 

 

código fuente

Hecho en Processing

Para poder realizar esta aplicación, se necesita algún tipo de memoria que guarde las posiciones, colores y tamaños de cada uno de los círculos que son creados para luego poder dibujarlos en forma actualizada. La estructura general de esta aplicación es la siguiente:

 

void setup(){

Inicializar la memoria

}
void draw(){

if( mousePressed ){

if( Hizo clik sobre un círculo ya existente ){

Cambiar el aspecto del cículo seleccionado

}

if( No toco ningún circulo ){

Almacenar en memoria un nuevo círculo

}

}
Dibujar los círculos almacenados en memoria

}

//El texto en cursiva no es ejecutable, es sólo una síntesis textual

El esquema anterior no es código ejecutable, sino un esquema que combina código ejecutable y explicaciones literales para mostrar la estructura del programa. Luego veremos que las explicaciones literales serán reemplazadas por código ejecutable, de hecho, en el código fuente del ejemplo hay comentarios que indícan las partes correspondientes a estos literales. Si revisamos con atención el esquema anterior veremos que la idea es crear y actualizar una memoria donde se almacenan las posiciones, colores y tamaño de los círculos que hay en pantalla. Luego un parte de la estructura se encarga de dibujar en cada ciclo del void draw() a los círculos que hay almacenados hasta el momento.

Cabe aclarar que la instrucción if( mousePressed ){ está preguntando "si el mouse está siendo precionado", dado que mousePressed es una variable de estado que permite saber si el mouse está siendo presionado o no. Esta variable es boolean y devuelve true cuando el mouse está siendo presionado, y false cuando no lo está.

La forma en que se implementa esta memoria es utilizando arreglos. Para la realización de esta aplicación es necesario crear cuatro arreglos que sirven para almacenar la posición, color y tamaño de los círculos. Estos arreglos se declaran fuera del void setup() y del void draw() para que sean globales, es decir, para que puedan ser visto desde cualquier parte del algoritmo. Si por el contrario, fueran declarados dentro de alguna de estas estructuras (por ejemplo el void setup() ) no podrían ser accedidos desde fuera de dicha estructura. También se declaran dos variables: limite y cantidad, que son utilizadas para especificar la cantidad límite de círculos que se pueden crear (imite) y la cantidad creada hasta el momento (cantidad). El código a continuación es el necesario para inicializar la memoria :

 

float[] x , y , tinta , radio ;
int limite;
int cantidad;

void setup(){

    size(400,400);
    limite = 50;
    cantidad = 0;
    x = float[limite];
    y = new float[limite];
    tinta = new float[limite];
    radio = new float[limite];
    ...

Hecho en Processing

La variable cantidad funciona de la siguiente manera: cuando inicia la aplicación la variable tiene valor igual a cero. Cuando el usuario hace click, si no se ha tocado ningún círculo, se genera un nuevo círculo. Se le asigna la posición tomando las coordenadas del cursor y cargándola en los arreglos x e y. El color y el tamaño se establecen asignandoles valores al azar a los arreglos tinta y radio, respectivamente. Cuando el círculo que se agrega es el primero, el valor de cantidad es cero y por lo tanto los valores del primer círculo se carga en la primer celda de los arreglos, es decir en la que tiene índice igual a cero. Pero inmediatamente después se incrementa el valor de de la variable cantidad, esto se hace con dos fines: para registrar la cantidad de círculos almacenados y para saber el siguiente lugar disponible para cargar un círculo. El siguiente código es el que sirve para almacenar en memoria un nuevo círculo :

 

...
if( mousePressed ){

    ...
    if( ! tocoAlguno && cantidad<limite ){
      x[cantidad] = mouseX;
      y[cantidad] = mouseY;
      tinta[cantidad] = random(255);
      radio[cantidad] = random(20,80);
      cantidad++;
      ...

Hecho en Processing

Para ilustrar lo que hace el anterior código, imaginemos el sguiente proceso: cuando la aplicación se inicia por primera vez, la variable cantidad vale cero y aún no fue ingresado ningún dato a los arreglos. El sguiente gráfico muestra los cuatro arreglos vacios y la variable cantidad con el valor cero:

Cuando el usuario hace click y no se ha tocado ningún círculo, entonces se ingresan los valores de la posición, color y tamaño en los arreglos, usando el valor de cantidad como índice, es decir se carga el índice cero de cada uno de los arreglos. Por lo que x[cantidad]=mouseX se traduce como x[0]=mouseX, y se carga el valor de la posición horizontal del mouse en la primer celda (la celda con índice cero) de X:

Luego se incrementa en uno a la variable cantidad, por lo que pasa a valor uno. En este momento la variable cantidad indica exactamente la cantidad de datos cargados por arreglo, pero también indica el valor del siguiente índice donde se cargaran los nuevos datos:

Cuando el usurario vuelve a hacer click, vuelve a cargar los datos en el índice que indica la variable cantidad, pero en este caso x[cantidad]=mouseX se traduce como x[1]=mouseX, dado que ahora el valor de cantidad es uno:

Luego vuelve a incrementar la variable cantidad que pasa a valer dos, y nuevamente muestra la cantidad de datos cargados y el próximo índice a utilizar.

Dado que la variable cantidad sirve para saber, justamente, la cantidad de círculos almacenados hasta el momento, esta información es útil para recorrer los arreglos con un ciclo for a la hora de imprimir los círculos en pantalla, como muestra el código para dibujar los círculos almacenados en memoria :

 

...
for( int i=0 ; i<cantidad ; i++ ){

fill( tinta[i] , 255 , 255 , 100 );
ellipse( x[i] , y[i] , radio[i]*2 , radio[i]*2 );

}
...

Hecho en Processing

En el código anterior la variable que recorre los índices de los arreglos, es la variable i del ciclo for. En este momento, cómo cantidad vale dos, el ciclo for le asigna a la variable i los valores cero y uno, que son casualmente los índice que estan cargados en los arreglos. Así en la instrucción ellipse( x[i] , y[i] , radio[i]*2 , radio[i]*2 ) la cuando la variable i vale cero, se traduce como ellipse( x[0] , y[0] , radio[0]*2 , radio[0]*2 ). Y dado que x[0], y[0] y radio[0] valen 56, 108 y 30, respectivamente, entonces la instrucción queda finalmente así: ellipse( 56 , 108 , 30*2 , 30*2).

Por último, queda por ver cómo es que se selecciona un círculo ya existente. La forma en que se realiza esto es recorrer uno por uno los círculos y verificar si la distancia entre el mouse y el centro del círculo es menor al radio:

Para revisar la distancia, Processing tiene una función llamada dist(), a la que se le pasa 4 parámetros que son las posiciones X e Y de los dos puntos entre los que se quiere medir la distancia:

Así para revisar uno por uno las distancia del mouse a cada uno de los centros de los círculos, se usa un ciclo for que recorre con el índice i cada uno de los círculo y preguntar por la distancia al mouse: dist( x[i] , y[i] , mouseX , mouseY ) < radio[i]
El código que sigue sirve para verificar si hizo clik sobre un círculo ya existente y en caso de ser así, entonces cambiar el aspecto del cículo seleccionado :

 

...
boolean tocoAlguno = false;
for( int i=0 ; i<cantidad ; i++ ){

if( dist( x[i] , y[i] , mouseX , mouseY ) < radio[i] ){

x[i] = mouseX;
y[i] = mouseY;
tinta[i] = ( tinta[i] + 1 ) % 255;
tocoAlguno = true;
break;

}

}
...

Hecho en Processing

Dado que deseamos modificar un círculo por vez, es necesario cortar el cíclo for una vez que se encuentra un cículo. Esto lo hace la instrucción break. Esta instrucción interrumpe el ciclo for, por eso se ejecuta cuando el mouse cae dentro de un círculo y se actualizan los datos del círculo seleccionado.

La variable tocoAlguno sirve para verificar si algún círculo fue tocado por el mouse, en principio se le asigna un valor false, que sólo es cambiado si alguno de los círculos es tocado, en cuyo caso se le asigna el valor true. Esta variable sirve para la parte que almacenar en memoria un nuevo círculo, dado que sólo crea un nuevo círculo si el mouse no toco ninguno de los existentes, esto lo puede preguntar de la siguiente manera: if( ! tocoAlguno ){

ir arriba