Archive for December, 2004

3d en processing: 2, Sistemas de coordenadas

Sunday, December 12th, 2004

Segunda parte del tutorial de 3d básico en processing. En la primera parte planteamos algunas cuestiones relacionadas con el orden de los comandos y cómo éste afecta a la ejecución. Ahora veremos cómo se traduce esto en el trabajo en 3d, y comprobaremos que es muy poco intuitivo.

Funciones utilizadas:

box();
translate();
rotate();

1. Creación de un objeto en 3d:

Vamos a crear un objeto 3d básico en processing, por ejemplo una caja. No se me asusten los que vengan de actionscript. Es insultantemente fácil:


// Creamos una caja que mide
// 20px de ancho(x), alto(y) y largo(z)
box (20, 20, 20);

Al probar el código observamos que la caja no se ha creado en el centro del sketch. En processing, igual que en actionscript, el origen de coordenadas (0, 0) está arriba a la izquierda. La caja se crea con centro en ese origen:
Caja en 3d semioculta sobre fondo gris

2. Transformaciones: translate() y rotate():

¿Cómo cambiamos la caja de sitio? También muy fácil. Con la función translate():


// los  parámetros de translate son
// x=50 e y=50.
translate(50, 50);
box(20, 20, 20);

Cuadrado blanco sobre fondo gris
Ahora aparece en el centro. Aunque aparentemente es sólo un cuadrado, sigue siendo nuestra caja 3d. Si añadimos noFill() al principio podremos observarlo.

Creo que es buen momento para familiarizarse con translate() cambiando valores y observando los resultados (nótese que puede tener también 3 parámetros -x, y, z-).

Si queremos rotar nuestro objeto simplemente tendremos que utilizar otra función: rotate() (o para restringir a coordenadas específicas rotateX(), rotateY o rotateZ()). Un ejemplo:


// primero nos situamos en el centro
// de la pantalla
translate(50, 50);
// utilizamos rotate para rotar la caja.
// OJO: esta función no se expresa en
// grados sino en radianes
rotate(1.0);
// creamos la caja.
box(20, 20, 20);

Caja en 3d semioculta
Ya vimos en el anterior tutorial la importancia del orden de los comandos en processing. ¿Qué pasa si invertimos las transformaciones aplicadas en el último ejemplo?


rotate(1.0);
translate(50, 50);
box(20, 20, 20);

Caja en 3d semioculta
Como se observa, la transformación no da el mismo resultado. En teoría debería ser igual rotar un objeto y luego desplazarlo que hacerlo al revés. El estado final debería ser el mismo. ¿Por qué pasa esto?

Vamos a retrasar la respuesta hasta haber planteado otro problema en la siguiente sección.

3. Más de un objeto a la vez.

Veamos cómo lo haríamos para crear 3 cajas en distintas posiciones. Comenzamos creando una caja en unas coordenadas específicas:


// VARIOS OBJETOS:
// Caja 1 (Negra)
// Para situar una primitiva 3d en un lugar
// en el espacio, hacemos una traslación del
// sistema de coordenadas con translate(x, y, z);
// color de linea rojo
stroke(255, 0, 0);
// color de relleno negro
fill(0);
// traslación de coordenadas.
// x=30, y=30, z=10;
translate (30, 30, 10);
// y creamos una caja
box (15, 10, 50);

Caja en 3d semioculta
En el mismo archivo creamos una segunda caja:


//Caja 2 (Gris)
// Si ahora hacemos otra caja y especificamos
// exactamente las mismas coordenadas en el
// translate, la caja deberia aparecer en el mismo
// lugar, y por tanto solaparse con la anterior...
// caja gris
fill(127);
translate (30, 30, 10);
box (15, 10, 50);
// ...pero no lo hace.

Caja en 3d semioculta
¿Qué ha pasado? Nuestra caja debería aparecer en el mismo lugar que la anterior pero aparece más abajo y a la derecha.

La razón es la siguiente: La función translate() no transforma las coordenadas de un objeto específico, sino las coordenadas del “universo” completo de la pantalla. De este modo cuando vamos alterando sucesivamente los parámetros x, y, z las transformaciones se van “acumulando”.

Para comprenderlo analicemos los movimientos en el sistema de coordenadas de nuestro ejemplo:

La caja negra está a 30 pixeles (tanto en x como en y) de el origen de coordenadas (esquina superior izquierda) y 10 pixeles más “cerca” de nosotros (eje z) que éste. Si observamos detenidamente la segunda caja podemos ver que ésta ha efectuado las mismas transformaciones pero respecto al nuevo sistema de coordenadas (es decir, respecto a x=30, y= 30, z=10). La posición de la nueva caja respecto al origen de coordenadas original es, pues:

x= 30+30= 60,
y= 30+30= 60,
z= 10+10= 20.

**Para comprobarlo podemos hacer un nuevo sketch que sólo contenga una caja con translate(60, 60, 20) y se observará que la posición de esta es igual a la de nuestra caja gris del ejemplo.

…y finalizando con nuestro sketch de 3 cajas, aplicaremos lo último aprendido. Sabemos que las coordenadas de nuestro sistema ahora tienen origen en la segunda caja. Si queremos colocar una nueva justo debajo, tan sólo tendremos que alterar el eje y:


 // Caja 3 (Blanca)
fill(255);
translate (0, 10, 0);
box (15, 10, 50);

El sketch completo junto con el código fuente puede observarse aquí:
Caja en 3d semioculta

Comentarios:

Acabamos de encontrar dónde radica la dificultad (al menos para el que escribe) de trabajar con varios objetos en 3d en processing: En vez de tener un sistema de coordenadas fijo para situar los elementos especificando su posición absoluta, tenemos que ir colocandolos respecto a la última transformación. Por eso en el apartado 2 de este tutorial la rotación y la traslación daba resultados distintos según el orden en que las ejecutáramos. Teniendo esto en cuenta, no es difícil imaginar que la programación puede hacerse innecesariamente compleja con mucha rapidez.

Esta es la razón principal que ha inspirado este tutorial. En los dos siguientes capítulos ofreceremos dos soluciones. Una obtenida mediante comandos de processing (aún muy poco intuitiva) y la otra en forma de librería externa, que proporciona un sistema mucho más fácil de comprender.

Es todo por esta entrega. Como siempre, comentarios, correcciones, sugerencias y palabras en general son bienvenidos. Hasta la próxima.

tags:No tags

¡Publica!

Monday, December 6th, 2004

9. It all comes down to output.

No matter how cool your computer rendering is, no matter how brilliant your essay is, no matter how fabulous your whatever is, if you can’t output it, distribute it, and make it known, it basically doesn’t exist. Orient yourself to output. Schedule output. Output, output, output. Show Me The Output.

Michael Mcdonough, en el imprescindible decálogo: Top ten things they never told me in design school.

Todos tenemos un cajón de geniales ideas, bocetos, proyectos, canciones, diseños, artículos, experimentos… Si no eres capaz de publicarlos, distribuirlos, y hacer que se conozcan, básicamente no existen. Orientate a publicar. Sácalos.

tags:No tags

Nuevos instrumentos musicales

Sunday, December 5th, 2004

“… the mouse is probably responsible for programming, cutting, pasting and editing 99 percent of today’s commercial music”

Tiene gracia. Probablemente sea cierto, y no sólo con la música.

Lo dice el músico, periodista y “doblador de circuitos“** Chachi Jones, en una entrevista en disquiet.

**El circuit bending consiste en modificar circuitos de intrumentos musicales electrónicos (habitualmente de juguete o muy baratos) para conseguir resultados distorsionados, aleatorios o simplemente distintos de aquellos para los que estaba diseñado el aparato. Las maravillas del hazlo tu mismo…

Más sobre esto en próximas entregas.

tags:No tags

3d en processing: 1, Flujo de ejecución

Wednesday, December 1st, 2004

Esta es la primera parte del tutorial de 3d básico en processing. Es una introducción general y he intentado q sirva independientemente del resto de los pasos: no contiene nada de 3d.

Para probar el código basta con copiarlo y pegarlo en el editor*. Al principio de cada tutorial incluiré enlaces de las funciones utilizadas a la referencia en castellano de processing, amablemente traducida por Pedro Alpera.

*Nota: Al pegar en el editor de processing ejemplos de código copiados de una página web éste se cuelga (probablemente por los datos de formato), por lo que es mejor copiar el código fuente en los applets o simplemente escribirlo.

Funciones utilizadas

rect()
fill()
stroke()

1. Flujo de ejecución:

Dado que en processing carecemos de la típica línea de tiempo de flash o director, el orden de ejecución del código sólo va a estar determinado por el orden en que lo escribamos (estamos hablando de código simple, sin funciones ni objetos). Lo primero que hayamos escrito se ejecutará primero, y así sucesivamente.

Ejemplo 1:


// SECUENCIALIDAD DEL CÓDIGO EN PROCESSING 00
// El cuadrado de arriba a la izquierda es el primero
// en ser dibujado, y queda más al fondo.
rect(15, 15, 50, 50);
// el siguiente queda en la mitad
rect(25, 25, 50, 50);
// y el de más abajo es el último y queda al frente
rect(35, 35, 50, 50);

El resultado es el siguiente:
3 cuadrados blancos solapados sobre fondo gris

Aunque parezca trivial, no lo es. Si por ejemplo quisieramos crear un cuadrado y rellenarlo de un color, lo haríamos en ese orden, ¿no?


rect(15, 15, 50, 50);  // crea un cuadrado
fill(0); // lo rellena del color negro...?

Efectivamente, no. Como se comprobará si se prueba el ejemplo, el cuadrado permanece blanco (que es el color en el que se dibuja por defecto). En processing es necesario hacerlo al revés: primero se especifica una propiedad y luego el objeto al que ha de ser aplicada.


fill(0);   // ahora si, especificamos el color negro.
rect(15, 15, 50, 50);   // y se aplica a el cuadrado.

2. Persistencia de las propiedades

Una vez establecida una propiedad (como fill() o color() ), ésta se aplica a todos los objetos siguientes (hasta que se especifique lo contrario):


fill(0); //especificamos el color negro
rect(15,15, 34, 37); // creamos un cuadrado
rect(25,25, 10, 50); // y otro. los dos son negros.

¿Cómo se especifica lo contrario? Pues simplemente llamando de nuevo a la misma propiedad y cambiando el valor…


fill(0); //especificamos el color negro
rect(15,15, 34, 37); // creamos un cuadrado
rect(25,25, 10, 50); // y otro. los dos son negros.
fill(127); //ahora el relleno es gris
rect(10,10, 20, 20);  // y este cuadrado es gris

También se pueden anular algunas propiedades mediante comandos especiales (por ejemplo, noFill() para quitar el relleno o noStroke() para… bueno, creo que se entiende).

Un segundo ejemplo con todos los pasos combinados.
Ejemplo 2:


// SECUENCIALIDAD DEL CÓDIGO EN PROCESSING 01
// Dado que el código es secuencial,
// si queremos aplicar una propiedad a un
// objeto, tenemos que especificarla antes.
// Especificamos un color de relleno
fill(150);
// el primer cuadrado se rellena con ese color
rect(50, 25, 45, 45);
// especificamos un color de linea
stroke(240);
// Como no hemos cambiado el color de relleno,
// el siguiente cuadrado mantiene el gris del primero.
// El color de linea si ha cambiado.
rect(55, 30, 35, 35);
 // Ahora cambiamos el color de relleno
fill(200);
// El tercer cuadrado tiene un relleno distinto
// y mantiene el color de linea de el anterior.
rect(60, 35, 25, 25);
// y finalmente quitamos el relleno
noFill();
// y creamos un último rectangulo
rect(45, 45, 50, 50);

El resultado es este:
4 cuadrados con bordes y rellenos de diferentes colores

Y eso es todo por ahora.

Para saber más:

Acabando este tutorial me encontré uno muy parecido en los foros de processing, y es que quizás sea un poco inútil hacer más tutoriales sobre una herramienta con tantos recursos para aprender. En cualquier caso aquí está:
drawing statements and execution flow, part I

Hasta el siguiente.

Por supuesto, si tienen alguna duda, corrección, comentario o sugerencia, no duden en utilizar el formulario destinado a tal efecto. Justo debajo de esta línea :)

tags:No tags