GAmuza. Hybrid live coding/modular application Un programa para arte interactivo y electrónico 1 Contents 1. Introducción 1.1. Premisas 14 1.2. Qué es GAmuza 15 1.3. Instalación 17 1.4. ScriptEditor 20 1.5. Comandos de teclado 21 2. Interface. Módulos de aplicación GAmuza. Hybrid live coding/modular application. rel 04 © 2012 Emanuele Mazza Texto: Emanuele Mazza && María José Martínez de Pisón Dibujos: Carlos Maiques Valencia, España. Este texto forma parte de un libro en preparación sobre el entorno de desarrollo GAmuza Algunos derechos reservados. Reconocimiento - Compartir Igual (by-sa): Se permite el uso comercial de la obra y de las posibles obras derivadas, la distribución de las cuales se debe hacer con una licencia igual a la que regula la obra original. 2 9 23 2.1. Setting Manager 23 2.2. Interface Live Coding 30 2.2.1. Timeline 34 2.3. Tracking Video 35 2.3.1. Computer vision 38 2.3.2. Kinect 44 2.4. Análisis del audio en arte sonoro 49 2.4. Audio Stream (Input/Output) 52 2.5. Interfaces físicas 57 2.6. Arduino 60 2.7. Comunicación OSC 61 3 3. Lenguaje de programación 63 3.1. Elementos básicos de programación: datos, variables y funciones 65 3.1.1. Variables I 67 3.1.2. Funciones 69 3.1.3. Variables II 75 3.2. Funciones internas: formas geométricas básicas 3.2.1. Formas irregulares 4. Interactividad 134 141 4.1. Definiciones de interactividad 141 4.2. Código para interactividad. Eventos de ratón 146 4.3. Eventos de teclado 150 85 3.3. Estructuras de programación básicas 89 3.3.1. Expresiones aritméticas 89 3.3.2. Expresiones relacionales 90 3.3.3. Expresiones condicionales y operadores lógicos 90 3.3.4. Expresiones de iteración. Repetición 94 3.4. Color 4 77 3.9.3. Clases en GAmuza 98 3.5. Azar y probabilidad 103 3.5.1. Noise 108 3.6. Textos y tipografía 111 3.7. Trigonometría 116 3.8. Transformar: traslación, rotación 121 3.9. Programación orientada a objetos. POO 124 3.9.1. Tablas [arrays] 125 3.9.2. Clases y objetos 132 5 6 7 1. Introducción Este texto se ha desarrollado en el contexto de una facultad de Bellas Artes tras dos décadas de enseñanza de arte electrónico y digital, lo que nos ha hecho conscientes de la dificultad, y también de la necesidad, de hibridar la capacidad de abstracción y atención que requiere la formación técnica, con el sentido crítico y especulativo que impulsa la formación humanista. Ambos pseudo-perfiles amalgamados se enfocan hacia el arte contemporáneo, lo que suma no pocas incertidumbres al estudiante que crece en esta emulsión. Para reducir tensión a la mixtura, la experiencia acumulada en docencia e investigación se ha vertido en el desarrollo de GAmuza, una herramienta pensada para impulsar el salto, solventar problemas y facilitar el aprendizaje. En el aula, como en la vida cotidiana, hay una presencia masiva de dispositivos técnicos de comunicación que, además de ser el reflejo comercial de una industria creciente, indican también un cambio de paradigma mayor que debería despertar la necesidad de comprender las nuevas relaciones que se están dando entre hombre y mundo. En este cambio de paradigma el lenguaje sigue teniendo un papel importante que involucra el desarollo de otros lenguajes. Durante las últimas décadas del siglo XX el arte se hizo eco del debate entre la prevalencia del lenguaje sobre la imagen o viceversa [picture turn <--> linguistic turn]. Hoy sentimos una polaridad similar entre aquellos debates artístico-filosóficos y el impulso del lenguaje científico, cuya lógica, consciente en parte de sus fallidos principios de verificación, ha ido filtrando las incertidumbres e indeterminaciones de una nueva ciencia que, poco a poco, (o mucho a mucho) va encarnando nuevos esquemas conceptuales, diagramas que dibujan en el horizonte una nueva imagen-mundo, enunciada y representada con el lenguaje numérico, informático. Las conexiones que el hombre establece hoy con las imágenes-mundo, pasan por las “sinuosidades de nuevos campos de relación”1 que llevan hasta el espacio cotidiano la posibilidad de acceso a las 1 Vilem Flusser (2005) “La sociedad alfanumérica” en Revista Austral de Ciencias Sociales nº 9, pág. 105. 8 9 Introducción GAmuza Hybrid live coding / modular application investigaciones sobre partículas del universo, el principio de Incertidumbre de Heisemberg, la teoría de La base del pensamiento visual y la percepción espacial se fundamenta en estructuras geométricas las Catástrofes de René Thom, la teoría del Caos de Ilya Prygogine, y muchas otras que han incidido cuyo origen también está vinculado a las ciencias ópticas y el estudio de la luz. Paul Virilio nos dice que en ese cambio de visión y comprensión del mundo. actualmente hay otra óptica, otra luz y otra transparencia, “trans-apariencia electrónica [que] prepara A partir del Teorema de Nysquist, la base numérica de la imagen digital permite imaginizar sistemas o situaciones complejos de forma dinámica, de las muchas repercusiones que este hecho ha tenido, destacamos cómo el proceso ha ganado importancia sobre el resultado, dejando visibles los pasos que el pensamiento traduce en términos de programación. No hay límite en el lenguaje ni en el pensamiento, hay un discontinuo work in progress, que debería estar vinculado o impulsado por el marco social y cultural, como reflejo de una época que no puede ya ordenar linealmente los acontecimientos, no puede reflejar fijamente lo cambiante, y abre redes, ramificaciones e interconexiones. No hay simples códigos trabajando cuando las personas interactúan con las imágenes, entre ellos está su cultura, o su contexto. Esta es la razón por la que he insistido en la noción de visualización (con especial referencia a lo que hacen los seres humanos), tanto en la creación como en la respuesta, tanto en la generación como en el reconocimiento hay intersecciones entre conocimiento, información y pensamiento2. Podemos quedarnos a contemplar la apariencia de esa imagen-mundo como usuarios, enganchados a los dispositivos técnicos de comunicación, o saltar la valla, no sin esfuerzo, para observar o experienciar lo que conlleva esa espacialidad y temporalidad diferentes, moverse por niveles de complejidad, caos, azar e iterabilidad. geometría nació con una sombra arrojada en la cambiante superficie de la arena del desierto, ¿cómo fijar un punto allí donde toda huella es borrada por el viento? Y de la luz que arrojó aquella sombra comenta: Cualquier óptico te dirá que la división claro-oscuro no es distinta más que en circunstancias excepcionales. Y, con precisión, cuando no hay atmósfera, cuando no hay fluidos turbulentos (...) En la atmósfera, nunca homogénea, nunca isótropa, los medios locales están distribuidos de forma múltiple, de suerte que los rayos, así puede decirse, buscan fortuna en el mundo. Se quiebran por doquier y ejecutan un recorrido caprichoso. Contornean pues los obstáculos y producen un clarooscuro, un confuso-distinto. Y es así como se ve lo más comúnmente del mundo. No se necesita, donde sea y siempre, la presencia del sol. La luz se difunde. Si el ver es un modelo del saber, el conocimiento es casi siempre difuso. En absoluto confuso y oscuro, sino multiplicado por franjas y difracciones.4 También el sonido y la música tienen una base matemática y generan formas geométricas sinuosas, ondas sujetas a franjas y difracciones similares a las de la luz, pero que oscilan en longitudes distintas y con velocidad diferente. Si para el ser humano el mundo es lenguaje (o no podemos tener otra imagen del mundo que la *** Tal vez esta simplificación de situaciones pueda servir para clarificar inicialmente posiciones, pero no podemos plantearnos el desarrollo del pensamiento siguiendo polaridades: pensamiento científico por un lado y filosófico por otro. No. Como veremos, fue Leibniz quien acuñó los términos «función», «variable», «constante» y «parámetro» en un momento en el que, como hoy, el análisis de la lógica pertenecía tanto al campo de la filosofía como al matemático. Los sistemas de análisis filosófico, desde Moore o Russell, inspeccionaban todo aquello en lo que se detiene el ojo de la mente, fragmentándolo, observando las relaciones de esos pequeños trozos de mundo; desarticulando lo complejo en partes significativas más simples, desde los objetos materiales a los datos de los sentidos, para articularlo después por medio de un análisis conectivo. Este mismo sistema de análisis es la base para introducirnos y aprender a desarrollar estructuras complejas con los lenguajes numéricos (alfanuméricos) de programación. Nuestro propósito es clarificar cómo el pensamiento traduce las situaciones en algoritmos, utilizando estructuras de código significativas comentadas con imágenes de resultados, explicación del proceso y su relación con referentes artísticos. 2 Burnett, Ron (2004). How images think, Massachusetts: The MIT Press, Pág. 203 [texto on-line] [06.08.2012] <http://www. learndev.org/dl/HowImagesThink.pdf> 10 un nuevo régimen de relaciones”3 Michel Serres se sitúa mucho más atrás, para decirnos que la que el lenguaje nos brinda), la hibridación y feedback del análisis de los lenguajes visuales, sonoros, lingüísticos e informáticos, permite generar otras formas sintácticas de expresión, que como el mundo actual, son más proteicas, cambiantes. El arte contemporáneo, como generador de la parte expresiva y simbólica de este mundo, ha estado atravesado desde los años 60 por estas nociones de feedback transdisciplinar, buscando nuevas relaciones entre esos lenguajes para modelizar imágenes, sonidos o acontecimientos. No vamos a desarrollar en este texto las teorías vinculadas a estos fenómenos, aunque en algunos momentos aludamos a ellas, lo que queremos subrayar es cómo convergen los modos de comprensión estéticos, filosóficos y científicos, a la espera de que la fascinación que nos procura esta imagen impulse a los estudiantes a superar las dificultades o rechazos que el campo de la programación les genera a priori. Y en ello va también un reconocimiento, porque nuestro propio impulso debe mucho a la herencia de Processing y openFrameworks, y también a las metodologías aportadas por Casey Reas, Ben 3 Paul Virilio, “Todas las imágenes son consanguíneas” [Texto on-line] <http://www.upv.es/laboluz/2222/textos/virilio.htm> [07.08.2012] 4 Michel Serres, (1991). El paso del Noroeste. Madrid: Debate, pág. 48. 11 Introducción GAmuza Hybrid live coding / modular application Fry, Daniel Shiffman, Zachary Lieberman, Theo Watson y Arturo Castro y las comunidades de ambos Por último señalar de nuevo que, en este contexto, el proceso toma mayor importancia que el objeto software, por eso los contenidos de este texto van a seguir en parte los pasos aportados por estos y acabado, y, aunque ahora tengas ante los ojos este libro materializado, o digitalizado, con una forma otros autores. concreta, es muy posible que nuestros ordenadores estén tramando ya otra versión de texto y software Así, en los primeros apartados del capítulo Lenguage de Programación se van a mostrar algunos elementos de forma comparativa con el uso del lenguaje en Processing. Pero más allá de las referencias concretas, su influencia está latente en el deseo de entender y trasmitir el código como algo diferente a un tutorial, a un manual de instrucciones, articulando datos informáticos con reflexiones estéticas que esperamos amplíe, limpie y mejore esta. Iterabilidad, inevitable cualidad de lo digital que como Sísifo remonta una y otra vez su peñasco. . y proyectos profesionales. Con este conjunto de nociones técnicas, teóricas e históricas esperamos avanzar hacia una situación que supere la disyuntiva planteada por Lev Manovich. Lo que no debemos esperar de Turing-land es un arte aceptable en Duchamp-land. Duchampland quiere arte, no la investigación de nuevas posibilidades estéticas de los nuevos medios. La convergencia no va a suceder.5 En la parte más técnica subyace el conocimiento de la naturaleza de los elementos que propician la interactividad, la generación de formas por el sonido, el movimiento de dispositivos por código, por lo que conoce bien cómo se construye la casa del arte digital, sus habitáculos y aperturas al exterior, su extenso jardín y el laberinto que contiene. Para mostrar la relación de esos elementos básicos con los referentes artísticos recurriremos a minuciosos dibujos que, a modo de ilustraciones botánicas, resaltarán en cada caso la cuestión a analizar, al tiempo que abren una puerta de fuga (backdoor) para salir por caminos diferentes a los del enmarañado mundo de los derechos de publicación. Respecto al habitual ¿cómo leer este libro?, el nivel de atención y orden de lectura es algo que depende del background de cada uno. Como dice Derrida, “el lenguaje escrito se dirige inevitablemente a más de una singularidad”,6 y si tú, lector particular, estás acostumbrado a utilizar software de programación, es posible que algunas descripciones de código te parezcan obvias; esperamos que eso no consuma tu deseo, piensa que otros posibles lectores se están iniciando, y que tanto GAmuza como este texto buscan facilitar el aprendizaje. Para abrir la linealidad del texto se han planteado algunas conexiones cruzadas entre determinadas prácticas que vuelven una y otra vez con construcciones de programación diferentes, conforme se van incorporando más estructuras en el texto. En estos casos cabe el juego de seguir las indicaciones de páginas para, más que comparar sus distintas construcciones, observar diferentes maneras de pensar un algoritmo. 5 Lev Manovich, “The Death of Computer Art” [texto on-line] [12.05.2012] http://www.manovich.net/TEXT/death.html. 6 Safaa Fathy, (1999) D’Ailleurs, Derrida. Gloria Films [Documental DVD] Y Derrida continúa diciendo: “yo sé que cuando eso esté escrito, y formulado en un idioma, y por lo tanto legible, cuando la huella sea descifrable, perderá la unicidad del destinatario, de la destinataria”. 12 13 Introducción GAmuza Hybrid live coding / modular application 1.1. Premisas Dilema GUI versus Code En el campo de la enseñanza del arte digital es común ver posiciones que defienden el uso de software libre, pero entre ellos se plantea otro dilema: el uso de software de programación gráfica, como Pure data, o enseñar código, línea de comandos, como Processing. 1.2. Qué es GAmuza Conscientes de estas premisas, GAmuza es un software híbrido que conjuga un entorno ScriptEditor con distintas aplicaciones modulares para el desarrollo de diseño interactivo, la realización de performances audiovisuales en directo y principalmente la enseñanza de arte interactivo o electrónico. Es open-source, se distribuye bajo licencia MIT10 y está desarrollado para Linux Ubuntu x86 y para Mac OSX 10.6 o superior. Puede descargarse en: http://www.gamuza.cc Hay un complicado entramado de relaciones y significancias entre ambas posturas que a veces parecen responder a campañas de índole casi publicitaria: graficos-amigables versus entornos de programación-hostil. El adjetivo tras el guión indica el tono y posicionamiento del discurso. De nuevo el debate entre imagen y texto (textual turn7 vs. pictorial turn8), y como hemos señalado antes, GAmuza opta por la hibridación. Las interfaces gráficas de usuario (GUI) de los módulos están programadas y diseñadas para facilitar procesos de comunicación con dispositivos de audio, vídeo o Arduino, pero no para ocultar conceptos o estructuras de programación, por eso incorpora un ScriptEditor, estableciendo una situación híbrida Live-Coding El núcleo central de GAmuza es el entorno Live-coding para hacer prototipos gráficos, performances o enseñar programación visualizando las modificaciones del código de forma casi inmediata. Está basado en el lenguaje de scripting integrable Lua11, (utilizando la librería Luabind12 y una versión modificada de ofxLua13) y comprende toda la API OpenFrameworks14 v.007, estableciendo conexiones de inputs/outputs entre las funciones y los módulos de aplicación. que aúna GUI y Code. Facilitar, para que los estudiantes, artistas o gente interesada no experta pierda el miedo a algo que comúnmente se califica como demasiado complicado. Pero lo complicado o difícil es solo lo que no se comprende. Por eso…. Tecnología para la gente …GAmuza no oculta los conceptos o estructuras. Comprender las herramientas que usamos todos los días, es el primer paso para no ser esclavo de la tecnología. Más allá del tipo de relación que los estudiantes de arte mantienen con las tecnologías de información, la gente en general está multiplicando el tiempo y tipo de usos con estos medios. Se ha hecho popular publicar ideas personales, intereses, conversaciones, gustos, opiniones, fotos, videos… Todo el mundo está en línea, desde su portátil o Smartphone, y en 5 minutos te hacen sentir que eres el webmaster de todas tus ficciones o vidas virtuales diferentes, constantemente conectado con todos tus amigos virtuales, y lo puedes hacer sin conocer la tecnología que estás utilizando, y esto significa un montón de cosas, una es “gente para la tecnología”, o en otras palabras, los medios están preparando “gente a la que vender tecnología”. Gamuza es sólo un software, pero creado con un concepto en mente: “Tecnología para la gente”, no lo contrario.9 7 Rosalind Krauss (1966), “Welkome to the Cultural Revolution” en October, Vol. 77 [artículo on-line] <http://www9.georgetown. edu/faculty/irvinem/theory/Krauss-October-77-1996-WelcomeToTheCulturalRevolution.pdf > [30.08.2012] 8 Liderado en gran medida por W. J. T. Mitchell <http://humanities.uchicago.edu/faculty/mitchell/home.htm> [30.08.2012] 9 Mazza, Emanuele, “Why Gamuza” [texto on-line] <http://gamuza.d3cod3.org/about/concept/> [11.05.2012] 14 Sistema modular GAmuza comprende distintas aplicaciones modulares fácilmente configurables a través de GUI. Cada uno de estos módulos puede activarse, o no, en función del tipo de trabajo a realizar y para reducir el consumo del equipo. Los módulos activos enlazan directamente con las funciones y variables de salida del ScrpitEditor. Actualmente los módulos son: • Computer vision • Sensor Kinect • Audio input, output • Arduino En resumen, GAmuza es un software que recoge y coordina de forma particular lenguajes (Lua y C++) y plataformas (openFrameworks, openCV) ya existentes, e incorpora otras librerías propias, para facilitar los primeros pasos de estudiantes y artistas en el campo de la programación creativa. Por otra parte, combina las dos vías habituales de programación: consola de código textual (ScriptEditor) vinculada 10 MIT License <http://mit-license.org/> [11.05.2012] 11 The programming language Lua <http://www.lua.org/> [11.05.2012] 12 Luabind es una librería que ayuda a crear enlaces entre C++ y Lua. Ver http://www.rasterbar.com/products/luabind.html [12.05.2012] 13 ofxLua es un addon de Open Frameworks para que funcionen scripts Lua incrustados dentro de una aplicación OpenFrameworks. Ver https://github.com/danomatika/ofxLua [12.05.2012] 14 openFrameworks <http://www.openframeworks.cc/> [12.05.2012] 15 Introducción GAmuza Hybrid live coding / modular application a las aplicaciones modulares que se ajustan mediante GUI, para que ese proceso de aprendizaje creativo sea más fácil, pero sin ocultar las estructuras de programación. Para hacer funcionar esta hibridación se han programado librerías específicas de GAmuza, que como veremos más adelante, se diferencian sintácticamente de las retomadas de otras plataformas. Retomando un término de Armando Montesinos, GAmuza más que un collage de lenguajes y plataformas, reconstruye un entorno de programación por medio de “zurcidos”, utiliza parches y genera tejido nuevo entrecruzando funciones, sin ocultar los fragmentos. La facilidad de uso de GAmuza reside en la simplificación del lenguaje Lua (en sí bastante ligero) y la comunicación de dispositivos externos por los módulos GUI, manteniendo la estabilidad y potencia del lenguaje C++. 1.3. Instalación GAmuza está programado para funcionar en mac OSX 10.6 (Snow Leopard) y 10.7 (Lion) y en Ubuntu Linux x86. Mac OSX Desde <http://gamuza.d3cod3.org> descargar la aplicación, abrir (montar) el archivo .dmg y arrastrar el icono de GAmuza al acceso directo de la carpeta Aplicaciones, tal como indica la flecha en la imagen. El proceso de instalación ha terminado. El lenguaje de programación en el ScriptEditor aglutina el uso de código en lenguaje Lua15 con funciones propias de openFrameworks16 y otras funciones específicas de GAmuza que comunican directamente con los datos de entrada y salida de las interfaces gráficas de los módulos. Es importante entender esta movilidad o flexibilidad de niveles y lenguajes. Si abrimos la carpeta de GAmuza veremos que contiene dos aplicaciones: ScripEditor y GAmuza. Ubuntu Linux x86 Descargar el archivo .tar.gz desde <http://gamuza.d3cod3.org>, abrir la ventana del Terminal y escribir: tar xvfz GAmuza-xxxx-Linux.tar.gz -- sustituid -xxxx- por el nº de la versión descargada de GAmuza Esto creará una carpeta llamada GAmuza, después cambiamos al directorio cd GAmuza Abrir la carpeta install y ejecutar el archivo cd install && sudo sh 00_install_dependencies.sh 15 The language programing Lua <http://www.lua.org/> [11.06.2012] 16 openFrameworks <http://www.openframeworks.cc/documentation/> [10.06.2012] 16 Después 17 Introducción GAmuza Hybrid live coding / modular application sudo sh 01_get_GAmuza_source.sh En algunas versiones de Linux a veces aparece un error relacionado con el archivo /usr/include/ c++/4.5/complex; En tal caso, editar el archivo sudo gedit /usr/include/c++/4.5/complex Ir a la línea 953, donde está la función template, inline complex, pow #ifndef __GXX_EXPERIMENTAL_CXX0X__ // _GLIBCXX_RESOLVE_LIB_DEFECTS // DR 844. complex pow return type is ambiguous. template inline complex ScriptEditor: • En Mac hay una aplicación independiente para escribir el código. • En Linux está integrado en el módulo Live Coding. Puede escribirse el código directamente ahí, o usar cualquier editor de texto, guardar el script como archivo .txt, .lua, o .ga y abrirlo desde el botón “Load script” situado en la parte inferior de Live Coding, igualmente pueden guardarse los scripts desde “Save script”. Setting Manager. • En Linux es una aplicación independiente que se ajusta antes de iniciar GAmuza, si previamente no se había configurado el proyecto. • En mac está incluida como una pestaña más de los módulos. Tras configurar el proyecto debe reiniciarse GAmuza Teclas de comando. • Hay atajos de taclado para Mac y Linux, y otros específicamente para Linux [ver página xxx] pow(const complex&amp; __z, int __n) { return std::__pow_helper(__z, __n); } #endif y comentar la función completa #ifndef __GXX_EXPERIMENTAL_CXX0X__ // _GLIBCXX_RESOLVE_LIB_DEFECTS // DR 844. complex pow return type is ambiguous. /* template inline complex pow(const complex&amp; __z, int __n) { return std::__pow_helper(__z, __n); }*/ #endif Después, ejecutar 02_install_GAmuza.sh – sudo sh 02_install_GAmuza.sh GAmuza tiene una configuración de aplicaciones diferente en Mac OSX y Linux x86. Las principales diferencias son: 18 19 Introducción GAmuza Hybrid live coding / modular application 1.4. ScriptEditor ScriptEditor solo está disponible en Mac. Al abrir la aplicación se activan tanto el editor de texto como GAmuza. Las dos son independientes pero vinculadas por OSC. Puede cerrarse una de ellas y la otra seguirá activa. 1.5. Comandos de teclado Para compatibilizar y optimizar las posibilidades de GAmuza se han programado comandos de teclado, es recomendable conocerlos antes de utilizar las interfaces de los módulos de aplicación. La combinación se realiza prioritariamente con la tecla Alt para evitar duplicidades con los sistemas operativos, pero manteniendo dentro de lo posible la letra habitual para facilitar su aprendizaje: Mac OSX y Linux Alt+f. Poner/quitar la aplicación a pantalla completa. Para que se active la segunda pantalla es necesario que esté en pantalla completa. Alt+j. Amplia/reduce el tamaño de ventana del previo en la interface Live coding. Alt+w. Mostrar/ocultar el script sobre la ventana de salida o el previo ampliado. Alt+t. Mostrar/ocultar el Timeline Alt+g. Reproducir/Parar el Timeline Alt+p. Imprimir el frame actual, hace falta una impresora conectada al ordenador. La interface del ScriptEditor sigue directamente el modelo marcado por el PDE Processing, como epígono de una de sus influencias directas. Principalmente aporta un editor de texto para el código que en su parte superior dispone un menú y barra de herramientas, y en su parte inferior una consola de salida. En el menú desplegable superior se encuentra: File (abrir, guardar, cerrar, imprimir...), Edit (deshacer, rehacer, cortar,copiar, pegar, borrar, seleccionar todo, encontrar, reemplazar...), Script (enviar a GAmuza, borrar consola), Tools (selector de color). Y en la barra de herramientas unos botones para facilitar las labores Alt+r. Procesa el script. Hay también un botón en la interface para hacerlo. Alt+d. Abrir un script guardado (aparece un cuadro de diálogo para seleccionarlo) Alt+s. Guardar el script, lo hace directamente en la carpeta GAmuza/data/scripts con el nombre “gamuzaScript_xxxxxxxxxx.lua” donde las x corresponden a: año mes día hora minutos segundos usuales: enviar a GAmuza, limpiar script, nuevo script, abrir script y guardar como y limpiar consola. Ctrl+x. Cortar (información solo utilizable en la propia consola live coding) El editor de texto tiene una columna numerada a la izquierda para facilitar la localización de errores. Ctrl+c. Copiar (información solo utilizable en la propia consola live coding) También asigna una serie de colores a términos que reconoce vinculados a: Ctrl+v. Pegar (información que proviene de la propia consola live coding) • Funciones y términos de GAmuza: naranja of.setColor(255) • Constantes, azul OUTPUT_W • openGL: funciones, azul turquesa y constantes: marrón pardo glBegin(GL_QUADS) • Strings (cadena caracteres entrecomillados): rojo “texto entrecomillado” • Comentarios: gris // comentario • Negro para todo lo demás. x = 125 20 Sólo en Linux Alt+a. Seleccionar todo el texto Alt+e. Limpiar editor (borrar todo el código) Alt+b. Ampliar el tamaño del cursor momentáneamente para verlo con facilidad. 21 2. Interface. Módulos de aplicación En realidad, la tarea de descifrar las historias una por una me ha hecho descuidar hasta ahora la peculiaridad más saliente de nuestro modo de narrar, a saber, que todo relato corre al encuentro de otro relato y mientras un comensal avanza en su fila, desde la otra punta avanza otro en sentido opuesto, porque las historias contadas de izquierda a derecha o de abajo hacia arriba pueden también leerse de derecha a izquierda o de arriba hacia abajo, y viceversa17 Italo Calvino Cuando se abre GAmuza, desde ScriptEditor o directamente, la primera interface que aparece es el módulo Live coding. En su parte superior hay una serie de rectángulos de color, estos rectángulos son las pestañas que indican los módulos que están activos y dan acceso a ellos. Tal como advierte Italo Calvino, toda estructura modular puede describirse siguiendo direcciones distintas. 2.1. Setting Manager Vamos a empezar a describir la última pestaña blanca de la versión para Mac OSX que corresponde al Setting Manager (en linux la aplicación SettingManager.app es independiente y está en la carpeta de GAmuza). Con esta interface se activan o desactivan los módulos y se ajusta su configuración. No es necesario, ni recomendable, tener módulos activos si no se van a utilizar, porque supone un consumo innecesario de los recursos del sistema. Al abrir GAmuza, un buen hábito de inicio es ir al Setting Manager, ajustar la configuración de GAmuza a las características del ordenador y las necesidades del proyecto. Pasamos a describir la interface del Setting Manager, a partir de su imagen general y analizando después paso a paso los elementos configurables de cada módulo. 17 Italo Calvino (1993), El Castillo de los destinos cruzados. Madrid: Siruela, pág. 37. 22 23 GAmuza Hybrid live coding / modular application 24 Interface. Módulos de aplicación 25 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application El primer grupo de opciones se refiere a los módulos GUI, simplemente hay que seleccionar o deseleccionar los módulos que se quieren activar o desactivar, clicando en el cuadrado que hay a la OUTPUT MAPPING izquierda de los nombres. Las opciones son. GAmuza genera una rejilla sobre la imagen de salida para MODULES el número de nodos de esa rejilla, desde un mínimo de 1 proyecciones de mapping. Con un slider se puede elegir SENSOR KINECT -- Tracking con el sensor Kinect (significa que el rectángulo de la proyección tiene un punto de COMPUTER VISION -- con otras cámaras que reconozca el ordenador la proyección según las necesidades), a un máximo de 20 (la AUDIO STREAM (INPUT/OUTPUT) -- análisis de entrada y/o salida de audio ARDUINO -- trabajar con el microcontrolador ajuste activo en cada vértice, con los que se puede deformar imagen tiene 400 rectángulos ajustables). Imagen con 8 nodos de mapping, 64 rectángulos ajustables. Arduino IMPORTANTE, si se activa el módulo Sensor Kinect, por ejemplo, y no está la Kinect conectada al ordenador, muy probablemente GAmuza se cerrará al no encontrar el dispositivo. Lo mismo se recomienda para los otros módulos, no deben activarse si no están los dispositivos apropiados conectados. SCREENS MAIN SCREEN: abre un menú desplegable para seleccionar la resolución de la pantalla principal. La resolución mínima para visualizar AUTOMATION (INSTALATION MODE) Opción pensada para la presentación de instalaciones. Permite seleccionar un script, “Choose script”, abriendo una ventana de diálogo para elegirlo dentro del ordenador. Si se activa “Autoload script on start” GAmuza se inicia con ese script. Y por último, si se activa “Auto fullscreen” al abrirse el programa, está directamente a pantalla completa y procesando el script seleccionado. GAmuza es 1280x 800 píxeles. COMPUTER VISION SECONDARY SCREEN: menú desplegable para seleccionar la resolución de la segunda pantalla, (se corresponde al tamaño de la ventana de salida). En ambos casos, elegir según la resolución de los monitores o proyectores que se utilicen. Las últimas opciones hacen referencia al uso de tarjetas que duplican o triplican el tamaño de la ventana de salida. Si hay una de estas tarjetas conectada y el ordenador la reconoce, GAmuza genera una ventana de salida para sus dimensiones. Si se cambian las dimensiones de la segunda pantalla hay que resetear los ajustes del mapping en el módulo de Live Coding. O cargar un archivo de mapping si previamente se había guardado uno para las nuevas dimensiones de pantalla. 26 Estas opciones se ajustan si el módulo Computer Vision está seleccionado. USE TESTING VIDEO ofrece la posibilidad de utilizar un archivo de video como test, es decir, desactiva la webcam o cámaras de video, y utiliza los algoritmos y ajustes de tracking video sobre el archivo de video seleccionado con CHOOSE VIDEO. La opción CHOOSE HAAR FINDER File permite elegir el algoritmo de reconocimiento de partes del cuerpo. Por defecto, GAmuza carga el reconocimiento de rostro frontal, para cambiarlo, al clicar esta opción se abre una ventana de diálogo y hay seleccionarla en la carpeta: GAMUZA/DATA/SETTINGS/HAARXML, o donde uno tenga ficheros .xml de haar cascade. 27 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application SENSOR KINECT ARDUINO Esta sección solo se utiliza si se ha activado el módulo Sensor Kinect. Como en los casos anteriores, esta sección del Setting Manager solo Las opciones son simples, se puede activar el uso de visión infrarroja, se ajusta si el módulo de Arduino está seleccionado. si no kinect utilizará visión normal. En el menú desplegable de abajo se puede desactivar el led frontal del sensor kinect (OFF), y si quiere utilizarse, se puede elegir el color del Led y si parpadea o no: BAUDRATE (57600 por defecto) y SERIAL PORT (el puerto serie). Puede utilizarse el software de Arduino para comprobar mediante el Serial Monitor cuál es el puerto asignado y seleccionar en GAmuza el mismo. AUDIO STREAM (INPUT/OUTPUT) Esta sección se ajusta solo si el módulo del mismo nombre está activado. Lo primero es seleccionar AUDIO DEVICE, el dispositivo de audio con el que trabajará GAmuza. El menú desplegable muestra los que están accesibles en el ordenador. OSC ENGINE Ajustes para el protocolo de transferencia de datos OSC, se introducen directamente en el campo de texto. Los dos primeros SENDING TO IP y SENDING TO PORT envían datos a un ordenador con el número de IP y puerto que se pongan. El último, RECEIVING AT PORT indica el puerto de este ordenador que recogerá los datos enviados desde otro. En la opción SAMPLING RATE (Frecuencia de muestreo) hay que seleccionar una acorde con el dispositivo: en la consola de texto del Setting Manager aparecen todos los dispositivos de audio que reconoce el sistema con el Sample Rate que soportan. En BUFFER SIZE, seleccionar el tamaño de Buffer con que se va a trabajar (cantidad de muestras almacenadas), 512 es un tamaño válido para la mayoría de los proyectos, porque no produce clics o chasquidos en el audio, si se trabaja con Bins o FFT (ver pág. xxx) En linux estas opciones se pueden modificar en el archivo de configuración setingManager.xml MIDI Aunque no existe un módulo de MIDI, se pueden recoger datos de estos dispositivos y hay funciones específicas de GAmuza para comunicar con ellos. MIDI INPUT MIDI DEVICE Vestax PC-CONTROLLER puede requir un tamaño mayor, hay que tener en cuenta que a mayor buffer más recursos de procesador consume. Tras seleccionar las opciones necesarias hay que guardar la configuración clicando Save en la parte superior de la interface. Diferencias entre Mac y Linux: Algunos ordenadores tienen la entrada y salida de audio por separado, si se selecciona el dispositivo de entrada, no se pueden generar sonidos de salida. La mayoría de las tarjetas de audio presentan la opción de entrada y salida juntas, por lo que permiten hacer ambas. Mac: se abrirá un cuadro de diálogo para seleccionar la capeta donde se quieran guardar los ajustes, GAmuza genera un archivo XML con la configuración. Antes de empezar a trabajar con GAmuza, clicar Restart (botón superior izquierdo) para que se reinicie el programa con los ajustes realizados. También puede cargarse un archivo de configuración que ha sido previamente guardado. Linux: se guardan los cambios directamente en el archivo gamuzaSettings.xml. Cerrar la aplicación SettingManager.app y volver a abrir GAmuza. 28 29 GAmuza Hybrid live coding / modular application 30 Interface. Módulos de aplicación 31 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application 2.2. Live Coding MAPPING SETTING Hace referencia a la rejilla de mapping con las siguiente opciones: • Activa/desactiva que se muestre la rejilla Los algoritmos son pensamientos, las motosierras son herramientas18. • Resetea los cambios (vuelve al estado regular) • Abrir una configuración previamente guardada. • Guardar la configuración del mapping Live coding significa programar en directo, un término vinculado tanto a los entornos de programación con un tiempo de compilación infraleve —‘just in time programming’ o ‘real time’—, como a la realización de directos de audio y/o imagen que utilizan esos entornos, y donde la presencia del código como imagen o sobre la imagen que generan, es uno de los rasgos que lo caracterizan. Los software de Live Coding, Fluxus19 y Zajal20, fueron los que más influyeron en el desarrollo de COLOR CORRECTION SETTINGS Ajustar ecolor y aplicar efectos: • GAMMA CORRECTION: ajusta la “sensación” de contraste de la imagen si se percibe blanquecina o “lavada”. • BRIGHTNESS: ajusta el brillo de la imagen. GAmuza. Fluxus es un entorno de progración en tiempo real basado en C++ / Scheme y desarrollado • SATURATION: satura o desatura el color de la imagen. por Artem Baguinski. Zajal es un lenguaje de programación creativo, basado en Ruby y que funciona • CONTRAST: ajusta el contraste. bajo openframeworks, desarrollado por Ramsey Nasser. Siguiendo sus pasos, en las primeras versiones de GAmuza, hasta rel.0398, el código se escribía directamente en el módulo de Live Coding, y aun es así en GAmuza para Linux. En la versión 041 para Mac OSX el sistema Live coding se hibrida un paso más, el código puede mostrarse • FILM BLEACH: emulación de una técnica de revelado analógico que produce contraste alto y saturación baja. También llamado Bleach Bypass. • FILM TECHNICOLOR: emulación del rango de color de los TV de tubo catódico de los 80’s. • FILM FORCE B&W: Los tres últimos filtros están relacionados y el ajuste de uno repercute en los otros. Este aporta otra forma para desaturar. en la ventana de salida, pero cuenta con • FILM WHITE EXPOSURE: ajusta la sensación de luz. un editor independiente, combinando • FILM WHITE DIFFUSION: emulación del uso de filtros de gelatina White Diffusion para suavizar sombras. las necesidades de las performances en directo con su uso para la enseñanza de programación. En el módulo de Live Coding existen también opciones de ajuste y, como en la interface anterior, pasamos a describirlas individualmente. VIDEO OUTPUT SETTINGS • Activa/desactiva la segunda pantalla. Para que se visualice GAmuza debe estar a Pantalla completa: Alt+f • USE VERTICAL SYNC: sincroniza el número de frames con la tasa de refresco de la tarjeta gráfica, para reducir defectos visuales, como bandas o parpadeos y tener frames constantes Bajo la ventana del previo de vídeo, hay un monitor de los fotogramas 18 Stephen Ramsay, Algorithms are Thoughts, Chainsaws are Tools. [video on-line] <https://vimeo.com/9790850> [04.09.2012] 19 (Fluxus) <http://www.pawfal.org/fluxus/> [03.09.2012] 20 Zajal, < http://zajal.cc/> [03.09.2012] 32 por segundo a los que está procesando GAmuza, si se selecciona Vertical Sync se puede observar el cambio. 33 GAmuza Hybrid live coding / modular application 34 Interface. Módulos de aplicación 35 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application 2.2.1. Timeline El Timeline21 no es exactamente un módulo de aplicación GUI, sino una ampliación del Live coding Los sistemas de tracking video están vinculados al para facilitar la programación, con la que se puede visualizar el tiempo, poner keyframes (fotogramas- campo de computer vision, su aplicación en el ámbito clave) y triggers (etiquetas). Las pistas de keyframe se definen con código en el ScriptEditor, utilizando del arte proporciona una información importante para la función de GAmuza ga.addTimelineKeyframes(), que lleva 4 parámetros: “nombreVariable”, explorar nuevos modos de conexión e interactividad mínimo de la variable, Valor máximo. del mundo digital. A través del ojo de la cámara, el “nombreVariable.xml” (genera un fichero que almacena los datos de esa línea del timeline), Valor Se pueden hacer tantas líneas de keyframe como funciones se pongan, en la imagen se han definido 2, cuyos nombres de variable son; posX y posY. La duración en frames se determina con la función ga.setTimelineFrames() (ver página xxxx). Una vez generada la línea, los keyframes se sitúan temporalmente clicando con el ratón sobre el frame deseado, se pueden desplazar con el ratón, o borrarlos. La transición entre un keyframe y otro se establece mediante unas reglas de interpolación que que se visualizan clicando con el botón derecho sobre el keyframe, mostrando las siguientes opciones: • lineal • curva sinusoide • circular • parábola de una función cuadrática • cúbica • quíntica (ecuación polinómica de quinto grado) • exponencial • técnicas de backoff • con rebotes • elástica En todas ellas se puede añadir la característica: entre el cuerpo físico del espectador y la representación sistema puede reconocer posiciones, movimientos, colores, gestos, rostros, dentro de su campo visual, siempre que las condiciones de iluminación sean favorables. Conociendo esos datos, se pueden programar aplicaciones ante las que el espectador puede interactuar de forma más intuitiva, denominándose por ello interfaces gestuales, es decir, se interactúa con la pieza sin tocar botones u otros interfaces físicos. En el arte interactivo y electrónico son numerosos los ejemplos de este tipo de aplicaciones, que también ha incrementado su presencia en las artes escénicas. Estos dos ejemplos mantienen una relación inversa entre obra y el espectador. En Bubbles , una de las piezas pioneras de arte interactivo con tracking video, el espectador juega con las formas proyectadas, modificando su movimiento al colisionar con su sombra. En Body Navigation23, el cuerpo de los bailarines genera las formas. Aunque en ambos casos, es el sistema el que las modifica o produce al conocer • entrada y salida suavizada o de los bailarines. activan un evento, actúan como etiquetas reconocibles, reduciendo mucho las líneas que deberían escribirse en el script si estos elementos, keyframes y triggers, no estuvieran. La línea inferior del Timeline actúa como un escalímetro para ampliar/reducir su tamaño.También puede regularse la altura de las líneas de keyFrame con el ratón 21 La base del TimeLine procede de James George, ofxTimeline <http://jamesgeorge.org/opensource.html> [12.08.2012] Basado en Bubbles, (2002) W. Muench y K. Furukawa 22 • entrada suavizada • salida suavizada La pista inferior se denomina Trigger. Los trigger son elementos que 36 2.3. Tracking Video con precisión la posición del cuerpo del espectador Basado en Body Navigation (2008) O. Kristensen y J. Jongejan El tracking de movimiento, colores o formas puede utilizarse también para generar o modificar sonidos, activar o desactivar dispositivos, simplemente hay que comprender su procedimiento. Por ello, a continuación se describe la interface del módulo Computer Visión que cubriría las primeras fases de este proceso: analizar determinadas características de algo exterior, recoger datos de su comportamiento, seleccionar y/o ajustar esos datos, para después generar con ellos otra situación que responda en tiempo real a sus cambios. 22 Bubbles, (2002) W. Muench y K. Furukawa <http://at.zkm.de/node/193 > [10.07.2012] 23 Body Navigation (2008) O. Kristensen y J. Jongejan <https://vimeo.com/1362832 > [10.07.2012] 37 GAmuza Hybrid live coding / modular application 38 Interface. Módulos de aplicación 39 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application 2.3.1. Computer vision GENERAL SETTINGS FLIP SOURCE IMAGE voltea la imagen-video que proviene de la El módulo Computer vision24 se corresponde con la pestaña roja y facilita la aplicación de algoritmos para tracking video, permitiendo regular con sliders, menús y botones las condiciones básicas de entrada de datos al sistema. Como en el apartado Live coding, pasamos a describir su interface. El monitor de entrada de la señal video, COLOR INPUT, permite reducir el área a trackear, este crop se ajusta arrastrando con el ratón los puntos resaltados de los vértices. Los demás monitores muestran solo la zona seleccionada. El crop se resetea con RESET cámara, su ajuste depende de las condiciones de exhibición: • OFF: la imagen se percibe sin ningún cambio • HORIZONTAL: se selecciona cuando los movimientos ante la imagen responden al efecto de estar frente a un espejo. • VERTICAL: imagen video cabeza abajo. • HORIZONTAL + VERTICAL: voltea en los dos sentidos. SOURCE BLUR: desenfoque de la imagen de entrada de video. CONTRAST: contraste. BRIGHTNESS: brillo. QUAD WARPING. USE DEVICE. Activa/desactiva el módulo, cuando no se utiliza pero sí se usará después, sin tener que reiniciar el programa. UNDISTORT (LENS CORRECCTION), sirve para desactivar ajustes previos hechos con la aplicación Lens Correction25. El monitor BALANCED TRACKING muestra el resultado de todos los algoritmos de tracking seleccionados y ajustados con los sliders, menús y botones del bloque derecho. CAPTURE BACKGROUND, al clicar el botón se captura un frame de la imagen video que debe corresponder con el fondo. Esta imagen se BACKGROUND SUBTRACTION SETTINGS SUBSTRACTION TECHNIQUE, seleccionar entre: • COLOR ABS: color absoluto • B&W ABS: blanco y negro absoluto • LIGHTER THAN. Cuando la figura a trackear es más clara que el fondo. • DARKEN THAN. Cuando La figura a trackear es más oscura que el fondo. SUBSTRACTION THRESHOLD, ajusta el umbral de detección. toma como referencia para comparar los cambios que se producen BLUR, desenfoca la imagen procesada entre el valor de sus píxeles y el de los frames de la imagen a trackear, ERODE, reduce las superficies detectadas aplicando el algoritmo Background Subtraction. Es el primer nivel para el cálculo de Tracking, y siempre activo. Según las condicones DILATE, expande las superficies detectadas de íluminación, puede mejorarse la imagen activando USE CONTRAST STRETCH: incrementa el rango de los valores más intensos de la imagen 40 en escala de grises. OSC DATA SETTINGS El resultado de BACKGROUND SUBSTRACTION se ve en el monitor USE KALMAN FILTER. En la transferencia de datos a otras inferior. aplicaciones, el filtro de Kalman suaviza los cambios grandes entre 24 Las aplicaciones de tracking video se han articulado en dos interfaces diferentes por las particularidades técnicas del sensor kinect, por ello en este módulo nos referiremos al uso de cualquier webCam o cámara de vídeo conectada y que el ordenador reconozca 25 Lens Correction es una aplicación, disponble en la web de GAmuza, diseñada para corregir las aberraciones de las ópticas. establecidos. los valores obtenidos, desechando aquellos que superan unos límites SMOOTHING FACTOR ajustar el rango de suavizado. 41 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application USE COLOR TRACKING Las opciones de ajuste son: Color Tracking es un algoritmo de reconocimiento del color. Requiere condiciones de iluminación estables y la elección de un color de HUE, selecciona el tono a trackear según la escala de valores de GAmuza entre 0 y 1. La franja de color sirve de orientación. seguimiento muy diferenciado del contexto. HUE RANGE, amplía el rango del tono seleccionado. USE COLOR TRACKING, activa/desactiva su uso y los resultados se SATURATION, asigna el nivel de saturación del tono (hue) seleccionado. El valor 0 es igual a blanco, 1 es el tono puro, dependiendo del nivel de luminosidad (value). ven en el monitor inferior. SATURATION RANGE, amplia el rango de saturación seleccionado. COLOR TRACKING SETTINGS La configuración de este bloque solo se ajustan si está activado USE COLOR TRACKING. El Tracking Color transforma el espacio de color RGB al sistema HSV, porque asigna los vaores del tono del color de una VALUE, regula el nivel de luminosidad (value) del tono. El valor 0 es igual a negro, 1 es igual al tono puro, dependiendo del valor de saturación. VALUE RANGE, amplía el rango de luminosidad seleccionado. HSV BLUR, filtro de desenfoque de la imagen en HSV. forma más estable. HSV identifica cada tono con un número que va de 0ª a 360ª según su posición en el ERODE, reduce las superficies detectadas. círculo cromático; en GAmuza el rango va de 0 a 1 quedando su equivalencia tal como muestra la imagen. DILATE, expande las superficies detectadas. BLOB TRACKING SETTINGS Los blob son las zonas independientes que el tracking detecta según los algoritmos seleccionados, sus opciones de configuración son: MIN BLOB, tamaño mínimo de los blobs en píxeles. MAX BLOB, tamaño máximo. CONTOUR DETAILL, el detalle del contorno se puede ajusta en: • RAW, todo el perímetro tal cual es • SMOOTH, suavizado • SIMPLE, adaptado a formas geométricas simples CONTOUR SMOOTH FACTOR, si se ha seleccionado smooth, ajusta el nivel de suavizado CONTOUR SIMPLE TOLERANCE, si se ha seleccionado simple, ajusta el rango de vértices que articulan el contorno. 42 43 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application MOTION DETECTION SETTINGS COMPUTING ALGORITHM SELECTOR El algoritmo para detección de movimiento está siempre activo. El En este bloque se seleccionan otros algoritmos específicos que se pueden aplicar, además de los monitor MOTION DETECTION muestra el cálculo de la cantidad de movimiento que se registra en la imagen y señala la posición media de ese movimiento. Los ajustes para su configuración son: mencionados hasta ahora. COMPUTE CONTOUR FINDER, para procesar el contorno de las zonas detectadas. Sus valores se ajustan con BLOB TRACKING SETTINGS. COMPUTE OPTICAL FLOW, calcula el flujo óptico del movimiento y lo • MOTION THRESHOLD, regula la sensibiliad del sistema para activar la detección, si es poco sensible se debe ampliar el umbral (Threshold) • MOTION NOISE COMPENSATION, compensa el ruido de la señal de imagen video • MOTION TRIGGER LOW LIMIT, nivel más bajo para activar la detección de movimiento • MOTION TRIGGER RANGE LIMIT, limita los niveles de movimiento, como si bajara la sensibilidad de detección. describe con vectores que indican la dirección e intensidad. COMPUTE HAAR FINDER, activa el algoritmo para detectar partes del cuerpo humano. Hay distintos algoritmos posibles y debe seleccionarse previamente en el Setting Manager (ver página XXX). Por defecto, está seleccionado FRONTALFACE_ALT. COMPUTE TRIGGER AREAS. Activa la posibilidad de utilizar 8 zonas circulares, que están numeradas para su identificación. Su posición y tamaño pueden ajustarse con el ratón; con ellas se pueden determinar las áreas en las que debe responder el tracking utilizando las funciones específicas de GAmuza para ello (ver página XXX) BALANCED TRACKING SETTINGS Si hay varias cámaras conectadas al ordenador, habrá un interface Computer Visión para cada una de Vuelve a ajustar, en conjunto, los valores dados en cada sección. una interface diferente que puede ajustarse independientemente. ellas, es decir, si hay dos cámaras conectadas al ordenador, habrán dos pestañas rojas, cada una abre • BALANCED TRACKING BLUR, ajusta el nivel de desenfoque global, • ERODE, reduce las superficies detectadas • DILATE, expande las superficies detectadas 44 45 GAmuza Hybrid live coding / modular application Interface. Módulos de aplicación 2.3.2. Sensor Kinect 46 47 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application La configuraación del módulo kinect mantiene muchas semejanzas con el de computer visión, excepto en las particularidades del sensor. RECORD DEPTH: Los dos sliders bajo el monitor permiten ajustar y GENERAL SETINGS SENSOR KINECT BLUR: grado de desenfoque de la señal de entrada visualizar el rango de profundidad a detectar: ERODE: reducir el área de las superficies detectadas • NEAR TRESHOLD: determina el plano de los elementos más próximos, como mínimo desde 70 cm del sensor. DILATE: ampliar el área de las superficies detectadas • FAR TRESHOLD: hasta qué plano de profundidad se quiere detectar, como máximo 6 m. BLOB TRACKING SETTINGS MIN BLOB: tamaño mínimo de los blobs MAX BLOB: tamaño máximo de los blobs CONTOUR DETAIL: menú con tres opciones, raw, smooth o simple DEPTH RANGE MASK: monitor que muestra el resultado de los algoritmos aplicados. USE KINECT HANDS TRACKING: activa el sistema de detección de manos, se representa en el monitor de la imagen anterior con puntos amarillos. CONTOUR SMOOTH FACTOR, si se ha seleccionado smooth, ajusta el nivel de suavizado CONTOUR SIMPLE TOLERANCE, si se ha seleccionado simple, ajusta el rango de vértices que articulan el contorno. COMPUTING ALGORITHM SELECTOR COMPUTE CONTOUR FINDER, para procesar el contorno de las zonas detectadas. Los valores se ajustan con BLOB TRACKING SETTINGS. COMPUTE OPTICAL FLOW, calcula el flujo óptico del movimiento y lo MOTION DETECTION SETTINGS: puede regular la detección de describe con vectores que indican la dirección e intensidad. movimiento según niveles de profundidad. COMPUTE CONTOUR GEOMETRY, determina formas geométricas a • MOTION TRESHOLD: porcentaje de datos de la detección de movimiento tortal anterior si no lo estaba. • MOTION TRIGGER LOW LIMIT: porcentaje de datos de movimiento en los planos próximos • MOTION TRIGGER RANGE LIMIT: porcentaje de datos de movimiento en los zonas alejadas. partir de los contornos. Al activarlo se activa automáticamente el COMPUTE TRIGGER AREAS. Activa la posibilidad de utilizar 8 zonas circulares, como en el módulo computer vision. SENSOR KINECT HARDWARE recoge datos de los sensores internos SENSOR TILLT: datos de inclinación ACCELEROMETER X, Y, Z: datos independientes del acelerómetro en los ejes x, y y z 48 49 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application 2.4. Análisis del audio en arte sonoro Lo natural es que todos los sentidos reciban información. Lo antinatural es el aislamiento de los sentidos.26 El sonido es un medio pasajero que se mueve en el tiempo, difícil de asir, pero la incorporación del audio en los medios digitales como un elemento casi propio, ha modificado nuestra relación con él, cambiando los hábitos de escucha y percepción del sonido. Sin perder su cualidad temporal se ha incrementado mucho su capacidad espacial. En el campo del arte estos cambios se ven reflejados en las instalaciones de arte sonoro o en las actuaciones de música con visuales, eventos que en muchos casos han ido borrando la distancia que los separaba, al incorporar sonido espacializado o propuestas sinestésicas entre audio, vídeo, luz y/o gráficos, estableciendo conexiones directas o indirectas ente la la materialidad de lo sonoro con la sonoridad de lo visual. El propio sistema Live coding en que se basa GAmuza es consecuencia o reflejo de estos cambios. Muchas de estas propuestas artísticas requieren el análisis de los parámetros del sonido. Para el sistema estos datos suponen solo valores numéricos que pueden aplicarse para producir otras situaciones sonoras, visuales o físicas, y es la casi simultaneidad de los cambios que se producen en el sonido analizado y en la respuesta generada la establece un efecto empático ente ambas. Métodos de medición se convierten en observaciones físicas. Los oídos se vuelven instrumentos ópticos; el movimiento de los ojos, una manera de entender el funcionamiento del sonido.27 Basado en la obra test pattern [nº3] (2010) de Rioji Ikeda28 Pasamos a describir la interface Audio Stream de GAmuza que analiza la entrada de sonido, obteniendo datos del pitch, volumen o FFT, y realizar otros ajustes para el tratamiento de la señal de audio. 26 José Manuel Costa (2011), “Arte sonoro” en Exit Express nº 54, pág. 26 27 Magnus Haglund (2005) “The air between the planets, on the audio and visual works of Carsten Nicolai”, en Carsten Nicolai Anti Réflex, Frankfurt: Max Hollein, pág 27. 28 Rioji Ikeda < http://www.ryojiikeda.com/project/testpattern/> [08.09.2012] 50 51 GAmuza Hybrid live coding / modular application 52 Interface. Módulos de aplicación 53 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application 2.4. Audio Stream (Input/Output) Antes de describir las opciones de la interface, señalar que este módulo GUI es solo para ajustar el análisis de entrada de sonido (Input Channel), no la salida. Si en el Setting Manager se ha activado el módulo Audio Stream (input/output) para generar sonido, y se ha seleccionado un dispositivo solo de Como en los otros módulos GUI, la transferencia de datos de audio por OSC puede ajustarse activando USE KALMAN FILTER. El filtro de Kalman suaviza la relación de los datos cuando presentan grandes cambios, cortando aquellos que superan unos límites establecidos. SMOOTHING FACTOR ajusta el rango de ese suavizado. salida, esta interface no estará visible, pero GAmuza mantendrá comunicación con los dispositivos de reproducción de audio. Los portátiles mac suelen tener dispsitivos separados para entrada y salida, por lo que es común esta situación, la mayoría de las tarjetas de audio, por el contrario, permiten El siguiente bloque de ajustes, PARAMETRIC EQUALIZATION, prepara ambas cosas a la vez. la señal para la transmisión de datos, ajustando el control individual Si el dispositivo seleccionado tiene varios canales de entrada, ancho de banda. veremos varios interfaces de Audio Input iguales que periten ajustes independientes, a cada uno de ellos se accede clicando en una de las pestañas amarillas. Debajo de las pestañas se indica el canal que estamos visualizando [CHANNEL 0], [1], [2]..., que será necesario poner como parámetro en muchas de las funciones de programación de audio. Si sólo se va a analizar la entrada de un canal, se pueden desactivar los otros con el botón USE INPUT CHANNEL. Debajo hay un slider para regular el nivel de volumen y un botón para silenciarlo. NOISE REDUCTION SETTINGS es un sistema para grabar el sonido ambiente que se desee reducir. Clicando REC NOISE, se inicia la grabación y clicando de nuevo se detiene. El slider, REDUX FACTOR, gradúa el nivel de reducción a aplicar. de tres parámetros, en tres bandas: frecuencia central, ganancia y • CENTER [BIN], selecciona la frecuencia central en Herzios. • AMPLITUDE [DB], controla la ganancia, determinando cuánto se amplifican o recortan esas frecuencias, por eso puede tener valores negativos, como muestra la imagen y el gráfico en la segunda banda. • WIDTH [Q], el factor Q determina la nitidez o calidad de la anchura de banda. Los ajustes se van visualizando en el gráfico PARAMETRIC EQ. El gráfico siguiente se corresponde a los valores ajustados en los sliders de la imagen derecha. Primero se busca la posición de la frecuencia central y se le ajustan la ganancia y el factor Q. NORMALIZED RADIAL BASIS FUNCTION NETWORKS (RBFN) es un algoritmo que genera un sistema de interpolación de datos para representar el filtro que se está aplicando a la señal de audio, en el dominio de la frecuencia. Este proceso se puede visualizar en el gráfico NOISE FILTER, tanto la grabación como el ajuste de reducción posterior. 54 55 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application La transformáda rápida de Fourier, FFT (Fast Fourier Transform), es un algoritmo muy utilizado para el VOLUME CHART muestra el gráfico y valores del volumen. Su rango de valores va de 0.0 a 1.0. La visualización de estos datos permite calcular mejor si deben escalarse, y por cuánto, para su uso en tratamiento de la señal de audio. En GAmuza opera sobre las muestras de sonido almacenadas en el BufferSize, por lo que su rango de valores depende del tamaño de buffer que se haya asignado en el SettingManager. programación. La función de GAmuza que recoge el volumen es Si hay un tamaño de buffer 512, el gráfico Filtered FFT BINS tendrá un tamaño [257], si el buffer es ga.getVolume(). 1024, ese valor cambia a [513], por lo que el algoritmo establece que el tamaño del FFT es el del PITCH CHART muestra igualmente el gráfico y valores del tono (pitch), su rango oscila entre 20Hz to 20000Hz, pero cuando los recoge la función de GAmuza, ga.getPitch(), los normaliza entre 0.0 y 1.0 BufferSize /2 +1, que serán los puntos de análisis de la frecuencia. En programación, estos datos se utilizan con la función ga.getFFT() que tiene dos parámetros: ID del canal de entrada de audio:[CHANNEL num], y la posición que ocupa la muestra (sample) en el Buffer, recorriéndolo desde 0 al valor de la variable global interna BUFFER_SIZE menos 1. Hay otros dos datos que GAmuza obtiene de este módulo: BIN y FFT. Los Bins provienen de la escala de Bark29, una escala psicoacústica propuesta por Eberhard Zwicker en 1961, que corresponde a la subdivisión de las primeras 24 bandas críticas del oído. Los márgenes de las bandas en Herzios son 0, 100, 200, 300, 400, 510, 630, 770, 920, 1080, 1270, 1480, 1720, 2000, 2320, 2700, 3150, 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500. GAmuza obtiene el valor de cada Bin desde el análisis del FFT. Para trabajar con estos datos se utiliza la función ga.getBin() y la variable interna FFT_BANDS. 29 Definición Bark scale [enciclopedia on-line] http://en.wikipedia.org/wiki/Bark_scale [25.08.2012] 56 57 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application 2.5. Interfaces físicas Cuando muchos de los estudios sobre arte digital auguraban un imparable crecimiento de lo virtual frente a lo físico, empezó a surgir toda una línea de trabajos en el campo del arte electrónico y el diseño que hibridaban lo físico y lo virtual, desarrollando interfaces físicas con un carácter muy diferente al teclado o el ratón para comunicarse con el ordenador o para comunicar los datos que genera o recoge el ordenador con otros dispositivos electro-mecánicos o electrónicos. El ordenador no solo puede recoger datos a través de la cámara de vídeo o el análisis del sonido, también mediante sensores -que detectan magnitudes físicas o químicas y las transforman en variables eléctricas-, un microcontrolador que haga de intérprete y un software, pueden recoger tantos tipos de datos como los que registran los sensores30. El microcontrolador más utilizado en el campo del arte electrónico para este tipo de trabajos es Arduino. Arduino es una plataforma de open hardware, basada en una placa con un microcontrolador y un entorno de desarrollo open source, diseñada para facilitar el uso de electrónica en proyectos multidisciplinares. hardware abierto significa tener la posibilidad de mirar lo que hay dentro de las cosas, que eso sea éticamente correcto, y que permita mejorar la educación. Educar en cómo funcionan las cosas…31 30 Tipos de sensores ênciclopedia on-line] <http://es.wikipedia.org/wiki/Sensor#Tipos_de_sensores> [30-08.2012] 31 David Cuartielles (2010) Arduino The Documentary, [video on-line] <https://vimeo.com/18390711> [30-08.2012] 58 59 GAmuza Hybrid live coding / modular application 60 Interface. Módulos de aplicación 61 Interface. Módulos de aplicación GAmuza Hybrid live coding / modular application 2.6. Arduino 2.7. Comunicación OSC El módulo Arduino responde a la pestaña azul. Esta interface establece la comunicación entre GAmuza y el microcintrolador Arduino32, las opciones de configuración son: Lectura de valores analógicos, del PIN 0 al 5. Cada monitor muestra el gráfico de la señal y su valor entre 0 y 1023. La función de GAmuza, ga.analogRead() normaliza ese rango de valores de 0.0 a 1.0. En la sección ANALOG PIN CONTROL, hay que seleccionar REPORT ON en los pin que se deseen leer y REPORT OFF en los no conectados. Arduino tiene 14 pin digitales, de los cuales 6 PWM (Pulse-width modulation) de 8 bits pudiendo transmitir un rango de valores de 0 a 254. Si se selecciona la opción Input u output funcionan como los demás, solo envían o reciben dos datos: HIGH o LOW. DIGITAL VALUES WRTING: los ajustes de los valores de escritura de los pin digitlaes y PWM, están vinculados a los datos programados con la función de GAmuza ga.digitalWrite(pwmPin,valor), el pin al que haga referencia la función debe estar activado en la interface. La interface de Comunicación OSC está siempre presente (no se selecciona desde Setting Manager) y responde a la pestaña de color gris. Su contenido varía en función de los módulos GUI que estén activados, estableciendo en su interface grupos de datos de cada uno de ellos, que presentan la opción de activar/desactivar el envío a otros programas de los datos individualizados que genera cada módulo. El sistema de recepción de datos por OSC se hace mediante código, no en la interface, y se recibe por el puerto indicado en el Setting Manager. 32 Arduino < http://www.arduino.cc/> [25.08.2012] 62 63 3. Lenguaje de programación La mayoría de las escuelas de arte dan clases de “arte digital”, que para ellos significa “cómo utilizar Adobe Photoshop”. A pesar de que en estos cursos suelen decir que exploran las posibilidades de un nuevo medio, por lo general exploran poco más que las posibilidades que alguien (es decir, Adobe) ha encontrado conveniente empaquetar en una pieza de software comercial. El hecho es que los ordenadores son capaces de hacer un número inimaginablemente mayor de cosas que cualquier pieza de software específico puede hacernos creer. […] es mi intención alentar a los artistas visuales a entender y trabajar más allá de las limitaciones impuestas por sus herramientas de software. […] me gustaría ofrecer una visión de lo que puede lograrse cuando un artista pasa de Photoshop por completo, y hace sus propias herramientas de software con código.33 GAmuza es un software desarrollado en una facultad de Bellas Artes en la que se ha vivido no solo el debate que refleja Golan Levin, sino también la dificultad de los estudiantes que, aun deseando utilizar código de programación para sus prácticas, les resulta difícultoso porque programar no implica solo adquirir un conjunto de conocimientos (recordar los detalles de la sintaxis), sino que precisa además desarrollar una habilidad de análisis y comprensión de conceptos abstractos complejos. Para enseñar a programar debemos distinguir distintos procesos: trasladar el problema planteado a la forma de un algoritmo y luego traducirlo al lenguaje del código. Entre ambas situaciones puede haber un proceso intermedio en el que se relacionan las partes significativas del problema con un repertorio de soluciones fragmentarias obtenidas en experiencias anteriores, este proceso intermedio requiere cierta experiencia previa. Por eso vamos a enfocar hacia ese nivel intermedio en el que los estudiantes empiecen a diseccionar y comprender los programas, para que posteriormente puedan relacionar esas unidades en estructuras cada vez más complejas, hasta llegar a escribir sus propios programas. Para ello se clarificarán métodos de análisis que traduzcan el problema en estructuras secuenciales significativas que ayuden a diseñar algoritmos. […] los expertos no sólo tienen más información, sino que la tienen mejor organizada. En lugar de percibir y recordar piezas individuales de información, procesan la información como grupos significativos, por lo que su percepción es más eficiente y su capacidad de recuerdo tiene un rendimiento mucho más alto.34 33 Golan Levin, (2001) ‘Essay for 4x4: Beyond Photoshop with Code’ [artículo on-line] <http://www.flong.com/texts/essays/ essay_4x4/> [12.05.2012]. 34 Katherine B. McKeithen et al., (1981) “Knowledge Organization and Skill Differences in Computer Programmers” Cognitive Psychology 13(3) Pág. 307. [Texto on-line] < http://141.213.232.243/bitstream/2027.42/24336/1/0000603.pdf > [12.05.2012]. 64 65 3.1. Elementos básicos de programación: datos, variables y funciones Pero todos estos procesos pueden carecer de sentido si el estudiante no visualiza la finalidad, es decir, referencias de dónde y cómo se aplican o se han aplicado procedimientos semejantes en resultados concretos. Lo que en el campo del arte entendemos como referentes artísticos, o en cualquier campo Un dato por sí mismo no constituye información, es el procesamiento de los datos lo que nos proporciona información35. como contextualización referencial. Aun conscientes de esta necesidad referencial, para poder iniciarse en la realización de aplicaciones hay algunas estructuras, funciones y expresiones básicas que deben conocerse en su conjunto antes de trabajar con ellas, por eso en las primeras páginas de este capítulo el número de ejemplos y referencias que nos clarifiquen la aplicación de código será un poco limitado. En cuanto esos elementos queden claros empezaremos ya a conectarlos con ejemplos aclaratorios, análisis de aplicaciones y propuestas que inciten su experimentación. Como anticipo nos detendremos en la Fórmula giratoria del Computer art que propone Jim Campbell36, con elementos de entrada (input), procesamiento y salida (output). Es común que las piezas de arte digital recojan datos del entorno, ya sean del contexto físico exterior o de la propia cabeza del autor, estos datos entran al ordenador (input) mediante un interprete (sensores, teclado, ratón…), son procesados de forma invisible (algoritmo, código) y producen una respuesta sensitiva (output). DATOS (INPUT) à PROCESAMIENTO à (OUTPUT) INFORMACIÓN Siguiendo esta fórmula, los datos de entrada al sistema pueden estar relacionados con el viento, llluvia, temperatura, tiempo, terremotos, stocks de mercado, niveles de luz…, pero más allá del origen de los datos, es necesario saber cuál es su naturaleza. Así, los datos del viento pueden ser numéricos (velocidad km/h), letras (direcciones N, S, E, O), palabras (nombres locales de los vientos), imágenes (fotografías o gráficos) o vectores (dirección e intensidad) La base del software es el procesamiento de datos, y necesita conocer su naturaleza porque la cantidad de memoria que debe reservar es muy diferente para un número o para una cadena de caracteres y mucha más para una imagen o para una gran cantidad de números organizados en matrices. La Memoria retiene la información de los datos para que puedan ser utilizados repetidas veces. Este espacio reservado de la memoria está vinculado con las variables. En la página siguiente se muestra un gráfico que despliega sobre el papel la fórmula del Computer art de Jim Campbel, fijando su movimiento. 35 Definición de Dato, [enciclopedia on-line] http://es.wikipedia.org/wiki/Dato [28.06.2012] 36 Jim Campbell, “Formula for Computer art”, http://www.jimcampbell.tv/portfolio/miscellaneous_references/ [28.06.2012] 66 67 3.1.1. Variables I Una variable es un como un contenedor para almacenar datos que después pueden ser reutilizados muchas veces en el programa y cuyo valor puede cambiar. El sistema necesita reservar determinada cantidad de bites de la memoria para que esa variable esté siempre disponible. Para comprender cómo se estructuran las variables en programación básica, y cómo se utilizan y diferencian los datos, vamos a comparar su uso en GAmuza y en Processing, por ser uno de los software más utilizados y documentados en este tipo de aplicación creativa. En Processing, cada variable tiene tres partes: • identificación del tipo de datos que guarda (int, float, boolean,…) • nombre (el que decida el programador) • valor (se asigna un valor inicial) Processing necesita hacer constar esa identificación, distinguiendo entre: int // Número entero float // Número decimal boolean // Un dato que sólo puede ser verdadero o falso. PImage char String PFont // Imagen // Carácter. El valor se define con apostrofes: ‘a’ //Cadena de caracteres. Se define con comillas “antes” // Fuentes tipográficas En GAmuza no hay que identificar el tipo de datos, si bien el programador debe ser consciente del tipo de datos que utiliza. En ambos software hay que declarar la variable y asignarle un valor antes de utilizarla, en el proceso del programa ese valor puede cambiar. Processing GAmuza int x = 50; x = 50 float y = 12.6; y = 12.6 boolean b = true; b = true Cada programador decide cómo nombrar las variables, pero algunos hábitos han generado una cuasinorma: • Se recomienda poner a las variables un nombre relacionado con su contenido, para que sea más fácil leer la programación y se reduzca la necesidad de comentarios. • Los nombres de las variables deben comenzar con una letra minúscula, y si hay varias palabras en el nombre, la primera letra de cada palabra adicional debe ser mayúscula (como tiempoActual) 68 69 3.1.2. Funciones Hay también algunas normas para nombrar variables: • El nombre de una variable no puede empezar con números • No puede coincidir con los términos propios del lenguaje de programación. Durante los siglos XIX y XX la partir de la Teoría de Conjuntos se amplió el rango de aplicación de Palabras reservadas en GAmuza (Lua)37 and break do else elseif in local nil not or end repeat false return for then Fue Leibniz quien en el siglo XVII acuñó los términos «función», «variable», «constante» y «parámetro». function true las funciones matemáticas, estableciéndose la actual definición de función como: la correspondencia entre los elementos de dos conjuntos dados, no necesariamente numéricos. if until while En programación, una función es un grupo de instrucciones que se ordenan y articulan con un objetivo particular y que se efectúa al ser llamada desde otra función o procedimiento. Las funciones deben tener un nombre único en su entorno de programación, una lista de parámetros de entrada asignada, Para evitar confusiones, las variables tampoco deberían coincidir con los nombres de las funciones internas del software utilizado, tales como of.line(), of.ellipse(), of.rect(),…. Otra cuestion importante en relación a las variables es el alcance o ámbito de la variable, es decir, donde puede ser utilizada en relación a donde se crea o declara, pero esto lo veremos después de entender qué es una función, ver página 77. su código y pueden devolver un tipo de datos como resultado. Las funciones pueden recibir datos desde afuera al ser llamadas a través de los parámetros (INPUT) y pueden devolver un resultado (OUTPUT). Algunas funciones pueden llamarse múltiples veces e incluso llamarse a sí misma (función recurrente). GAmuza, como todo software, requiere una terminología específica , para facilitar el aprendizaje, se ha mantenido su nombre lo más próximo posible al utilizado en otros lenguajes de programación creativa como Processing y openFrameworks. Debemos diferenciar entre funciones de bloque, funciones internas del software y funciones escritas por cada programador, pero para no complicar las definiciones vamos a ir señalando sus diferencias con ejemplos que muestren la estructura de programación. Empecemos viendo cuál es la estructura básica de un programa en el ScriptEditor. 37 Lua 5.1 Reference Manual, [texto on-line] < http://www.lua.org/manual/5.1/manual.html/ > [30.07.2012] 70 71 El sistema lee el código desde arriba hacia abajo y lo primero que aparece son los comentarios. En GAmuza, los comentarios pueden marcarse de dos maneras: • Con el código de Lua: -- • O con el habitual de Processing y C++: // /* GAmuza 0.4.1 examples --------------------basics/functionsTemplate.ga This example just show the environment functions; the basics setup(), update() & draw(), plus the listener functions to mouse&keyboard. Cuando el comentario ocupa varias líneas (bloque de comentario), se señalan al principio con /* y al final del comentario con */, o también puede hacerse al principio --[[ y al final --]] (código Lua). Las líneas de comentario con // o --, en ambos casos son barras consecutivas, pueden ponerse más de dos, pero si las dos primeras están más separadas el sistema no lo reconoce como comentario y da error, se puede detectar el fallo si el texto no adopta el color gris que el ScriptEditor les asigna por defecto. Los comentarios son textos que el sistema ignora, no los procesa, pero son importantes para el programador. El código de programación puede ser muy breve y fácil de leer o tremendamente extenso, por lo que es fundamental acostumbrarse a comentarlo bien y también a ordenar y tabular las líneas de código según niveles para facilitar su lectura y revisión. Tras los comentarios, están las variables globales, pero se describiran en la página 77, y después los bloques de funciones que estructuran y contienen el código, este código pueden ser funciones internas, procedimientos o expresiones, según las acciones que deban realizar. Por defecto, el nombre created by n3m3da | www.d3cod3.org */ function setup() // solo se ejecuta una vez end ------------------------function update() // donde se actualizan las variables, se lee en loop end ------------------------function draw() // donde va el código a dibujar, se lee en loop. end ------------------------function mouseDragged() end ------------------------function mouseMoved() de las funciones aparece de color naranja. end En GAmuza el final de cada bloque de función se señala con el término end, no signos de corchetes function mousePressed() {} que marquen principio y final.. El primer bloque es function setup(), cuyo rasgo distintivo es que, cuando se efectúa el programa, el código que se escribe ahí solo es leído una vez por el sistema. Contrariamente, el código que se escribe dentro de los bloques function update() y function draw(), el sistema los lee en loop, de manera que cuando llega a la última línea, genera un frame en la ventana de salida y empieza de nuevo por la primera línea de código, y así continuamente hasta que se apaga el programa. Además de los bloques setup(), draw() y update(), existen otros bloques de funciones usuales en programas interactivos, como los que aparecen en la siguiente plantilla de GAmuza: // cuando la acción se da al arrastrar el ratón // el código actúa cuando se mueve el ratón ------------------------// el código actúa cuando se aprieta un botón end ------------------------function mouseReleased() // actúa cuando se suelta el botón end ------------------------function keyPressed() //el código actúa cuando se presiona una tecla end ------------------------function keyReleased() //el código actúa cuando se suelta una tecla end ------------------------- Esta plantilla está disponible en menú File/Examples/functionsTemplate.ga 72 73 EXPERIMENTAL OPENFRAMEWORKS ADDONS Después de los bloques, el siguiente elemento importante son las funciones internas. En GAmuza, los nombres de estas funciones internas tienen diferentes prefijos según su origen: -------------------------------------------- of. Son funciones que provienen de openFrameworks, como of.setColor(). La norma establecida ofxArcText = ofx.arcText() ofxArcText para su escritura en el wrapping de GAmuza es of.functionName, es decir, si originalmente -- la función en openFrameworks es: ofSetColor(), en GAmuza se coloca un punto después ofxChromaKey --> extending ofImage de of y se inicia con minúsculas. Cuando el nombre de la función contiene dos conceptos, el ofxChromaKey = ofx.chromaKey() segundo empieza en mayúsculas dentro de la misma palabra, quedando of.setColor(). Todas -- las funciones terminan con (). El 98% de las funciones de openFrameworks está disponible en ofxDither GAmuza38. ofxDither = ofx.dither() ofx.Son algunas funciones que provienen de las api ofxAddons39. Su sintaxis en GAmuza es ofx. ofxDmx functionName, nuevamente es poner un punto después de ofx., empezar con minúscula ofxDmx = ofx.dmx() e iniciar en mayúscula el segundo concepto de la función. Dado que el wrapper de ofx no es ofxFTGLFont completo, se listan a continuación las clases ofx. disponibles en GAmuza40: ofxFTGLFont = ofx.FTGLFont() -- -------------------------------------------- ofxFX OFFICIAL OPENFRAMEWORKS ADDONS ofxBloom = ofx.bloom() -------------------------------------------- ofxBlur = ofx.blur() ofx3DModelLoader ofxBlurFast = ofx.blurFast() ofx3DBaseLoader = ofx.BaseLoader3D() ofxConway = ofx.conway() ofx3DModelLoader = ofx.ModelLoader3D() ofxFlocking = ofx.flocking() -- ofxFluid = ofx.fluid() ofxNetwork ofxGlow = ofx.glow() ofxTCPClient = ofx.TCPClient() ofxGrayScott = ofx.grayScott() ofxUDPManager = ofx.UDPManager() ofxMask = ofx.mask() ofxTCPManager = ofx.TCPManager() ofxOldTv = ofx.oldTv() ofxTCPServer = ofx.TCPServer() ofxWater = ofx.water() -- -- ofxVectorGraphics ofxGaussian ofxVectorGraphics = ofx.vectorGraphics() ofxGaussian = ofx.gaussian() -- -- ofxOpenCv ofxSubtitles ofxCvColorImage = ofx.cvColorImage() ofxSubtitles = ofx.subtitles() ofxCvGrayscaleImage = ofx.cvGrayscaleImage() -- -------------------------------------------38 Para un listado completo de las funciones de openFrameworks ver <http://www.openframeworks.cc/documentation> [03.06.2012] 39 ofxAddons. Un directorio de extensiones y librerías para OpenFrameworks. http://ofxaddons.com/ [03.06.2012] 40 ofx all classes [texto on-line] http://gamuza.d3cod3.org/examples/ofx-all-classes/ [03.07.2012] 74 ofxWordPalette WordWithSize = ofx.WordWithSize() ofxWordPalette = ofx.wordPalette() 75 ga. son las funciones internas programadas para GAmuza y que principalmente vinculan los datos de las interfaces gráficas de los módulos con el resto de funciones, también definen algunas particularidades del entorno de programación, como la función ga.background() que facilita un mejor sistema de transparencia en el fondo, y otras funciones de aplicación como ga.mouseX(), ga.mouseY() —devuelve el valor de la posición x e y del ratón sobre la ventana de salida—, ga.key() — devuelve el valor de la última tecla usada. Todas las funciones terminan con dos paréntesis que pueden albergar, o no, parámetros . Los parámetros se sitúan entre los paréntesis, separados por comas, y afectan el modo en que actúa la función; algunas funciones no tienen parámetros y otras pueden albergar más o menos parámetros según la situación, como: -- puede tener un rango de 0 (negro) a 1 (blanco) ga.background(0.0, 0.5) -- igual + transparencia (gris, alpha) ga.background(0.0, 0.0, 255,1) Una vez entendida la estructura, volvemos a las variables para comprender mejor dónde situarlas. Es necesario pensar dónde se declaran y asignan las variables, porque ello determina su ámbito de aplicación —dónde se puede acceder a ellas dentro del programa. Las variables declaradas dentro de un bloque sólo son válidas dentro de ese mismo bloque y dentro de otros bloques encerrados en ese 41 ga.background(0) 3.1.3. Variables II -- fondo azul opaco (R, G, B, alpha) Además de las funciones internas propias del software (que se analizarán después con más detalle), cada programador puede hacer sus propias funciones, como se verá más adelante, cuando entraremos en los detalles de la programación. bloque. Las variables declaradas en el nivel de base del programa —antes del setup() y draw()— pueden acceder a todas partes dentro del programa. Las variables declaradas dentro del setup() sólo se puede acceder a ellas en el bloque setup(). Las variables declaradas dentro de draw() sólo se puede acceder en el bloque draw(). El alcance de una variable declarada dentro de un bloque, se denomina variable local, y se extiende sólo hasta el final de ese bloque. El siguiente ejemplo muestra el alcance de las variables, en el código se utiliza una expresión condicional if que se verá en la página 92. d = 50 -- d y val son variables globales, val = 0 -- pueden usarse en cualquier lugar function setup() end function update() val = d * 2 end -- su valor es actualizado desde update() function draw() ga.background(0.0,1.0) of.setColor(val) y = 150 of.line(0, y, d, y) y = y - 25 -- y es variable local puede usarse solo en draw() -- d e y son llamadas desde draw() of.line(0, y, d, y) if d > 50 then x = 10 end -- x es variable local, solo se puede usar en bloque if of.line(x, 40, x+d, 40) of.line(0, 50, d, 50) of.line(x, 60, d, 60)-- ERROR x no se puede usar fuera del bloque if 41 Algunos textos los denominan argumentos. En nuestro caso mantenemos la nomenclatura utilizada en: Casey Reas y Ben Fry, (2007) Processing, a Programming Handbook for Visual Designers and Artist and. Cambridge(MA): The MIT Press. 76 end 77 Hay otro tipo de variables que se denominan variables del sistema, tienen un nombre asignado por el propio software, que le otorga ya su valor. En GAmuza se reconocen: De sistema Ancho de la ventana de salida OUTPUT_W Alto de la ventana de salida OUTPUT_H Traking video 3.2. Formas geométricas básicas A las esferas les inquieta constantemente su inevitable inestabilidad: comparten con la suerte y el cristal los riesgos de todo lo que se hace pedazos fácilmente. No serían formas de la geometría vital si no pudieran implosionar, si no fueran susceptibles de ser destruidas por la presión exterior; y menos lo serían si no estuvieran en condiciones de agrandarse bajo la presión interior del crecimiento de los grupos hasta convertirse en estructuras más ricas43. La programación creativa, por su vinculación a las artes visuales y el diseño, responde principalmente Altura de la captura de video CAPTURE_H Ancho de la captura de video CAPTURE_W Nº de pixels de la captura de video CAPTURE_PIX Máximo nº de blobs detectados MAX_BLOBS Nº columnas de optical flow OPTICAL_FLOW_GRID_X Nº filas optical flow OPTICAL_FLOW_GRID_Y a cuestiones visuales y espaciales. En este contexto, el primer elemento a comprender es el propio espacio de representación que queda definido por la ventana de salida. Sea cual sea su soporte, tipo de monitor o proyector de vídeo, debemos entenderlo como una matriz de puntos luminosos (píxeles) que, según los casos, albergará una representación 2D ó 3D de las formas visuales, y cuyo origen o punto cero se expresa mediante las coordenadas (0,0) ó, (0,0,0) y está situado en el vértice superior izquierdo de la ventana. Análisis captura de audio Nº de bandas FFT en la captura de audio FFT_BANDS Tamaño del buffer de audio BUFFER_SIZE Generación sonido Tipos de onda Notas musicales GA_SIN, GA_COS, GA_SAW, GA_TRI, GA_RECT, GA_NOISE, GA_PINK, GA_BROWN, GA_PHASOR, GA_PULSE DO_0,... // las 7 notas con números del 0 al 8 Trigonometría Radianes PI, TWO_PI, HALF_PI De openGL Las asociadas a las funciones y clases de openGL incluidas en GAmuza42, Centrándonos de momento en las formas 2D, para dibujarlas dentro de esa matriz es necesario pensar cuál será su ubicación, expresada en coordenadas (x, y), y las cualidades que permiten definirla. Hay funciones internas que facilitan el dibujo de figuras geométricas e incluyen en sus parámetros: las coordenadas y cualidades de la forma. En la siguiente tabla se muestran las funciones internas más comunes en GAmuza para dibujar figuras geométricas planas, lo que en programación se denomina Primitivas. 42 Información en openGL Documentation <http://www.opengl.org/sdk/docs/man/xhtml/> [26.08.2012] 78 43 Peter Sloterdijk (2003), Esferas I. Burbujas, Microsferología. Madrid: Siruela, pág. 50. 79 Antes de dibujar hay que tener en cuenta las características de la superficie y particularidades de las Formas geométricas 2D - Primitivas formas: color de fondo, así como el color y ancho del borde y si ese color las rellena o no. of.circle(x, y, 1) Punto -- círculo de 1 pixel de diámetro Funciones básicas, atributos de las formas -- (coordenadas del centro, diámetro) Color del fondo of.line(x1, y1, x2, y2) Línea ga.background(gris,alpha) ga.background(R,G,B,alpha) -- (coordenadas punto inicio, punto final) Esta función permite ajustar el color y transparencia del fondo, en escala de grises con dos parámetros (gris, alfa) o en color con cuatro parámetros (R, G, B, A). Todos los valores van de 0.0 a 1.0 Salvo excepciones, tiene que estar en el draw() of.triangle(x1, y1, x2, y2, x3, y3) Triángulo -- (coordenadas punto 1, punto 2, punto 3) Color de relleno Siempre hay que usarlo para definir el color cuando se va a generar algún gráfico. Si no se pone, no dibuja nada. -- puede hacerse también con of.circle(3) of.rect(x, y, ancho, alto) Rectángulo -- (coordenadas vertice, lado, lado) Si sólo tiene un parámetro funciona en escala de grises. 2 parámetros: escala de grises + alfa. 3 parámetros: color RGB y 4 parámetros: RGB + alfa. Los parámetros van de 0 a 255. Rellenar formas of.fill() No lleva parámetros -- ver RECT_MODE, página 82. Se utiliza si previamente hay un of.noFill() porque formas por defecto. of.circle(x, y, radio) Círculo of.setColor(0,0,255) No rellenar of.setCircleResolution(valor) GAmuza siempre rellena las of.noFill() No lleva parámetros -- el valor debe ser alto, si no dibula polígonos of.ellipse(x, y, ancho, alto) Dibujar borde No dibujar borde Elipse Ancho de la línea of.noFill() Por defecto no los dibuja of.setLineWidth(valor) Valor = grosor línea en pixeles of.circle(x, y, radio) Polígono of.setCircleResolution(valor) Suavizar bordes -- (5) -- el valor define el nº de lados del polígono of.enableSmoothing() No lleva parámetros No suavizar of.disableSmoothing() No lleva parámetros 80 81 Los rectángulos y cuadrados tienen diferentes opciones para determinar el punto de referencia de las coordenadas que controlan su posición, para ello se utiliza la función: of.setRectMode(). Algunos ejemplos permitirán revisar mejor lo hasta ahora visto. El programa tiene los tres bloques básicos de GAmuza. En el bloque setup() la función of.enableSmoothing() suaviza los bordes de las formas. Si las figuras giraran, apliacaría un ligero efecto anti-aliasing; al tratarse de una imagen fija no hace falta esa función. Por otra parte, cómo no se actualiza el valor de las variables, el bloque update() podría también no estar. Después se aplicarán /* en la página 91 cambios a las variables y todo cobrará sentido. GAmuza 0.4.1 examples --------------------- El bloque draw() es el único que contiene datos para los gráficos. La primera función determina Basics/OF_RECT_MODE.ga el fondo y su color; los parámetros (0.0, 1.0) significan negro y opaco, si fueran (1.0, 0.5) cambia el punto de referencia significaría blanco con 50% de transparencia. Los parámetros de la función ga.background() siempre en rectangulos y cuadrados van de 0.0 a 1.0. */ posXY = 300 La primera función of.setColor() tiene solo un parámetro, lo que indica que actúa en escala de lado = 400 grises, la segunda tiene dos parámetros, escala de grises (blanco) con transparencia. Los valores están function setup() compensados por lo que aparentemente dibujan el mismo color. of.enableSmoothing() of.setRectMode() es una función interna de openFramewoks que permite modificar las coordenadas end de referencia para la posición de rectángulos y cuadrados. Por defecto las coordenadas de la función of.rect() sitúan el vértice superior izquierdo, que corresonde al parámetro OF_RECTMODE_CORNER, function update() la variable OF_RECTMODE_CENTER cambia esa referencia al centro del cuadrado, por eso aunque los end parámetros de las dos funciones of.rect() son iguales, los cuadrados se posicionan en lugares diferentes. function draw() ga.background(0.0, 1.0) -- fondo negro sin transparencia of.setRectMode(OF_RECTMODE_CENTER) -- referencia: vertice superior izq. of.setColor(126) of.rect(posXY, posXY, lado, lado) of.setRectMode(OF_RECTMODE_CORNER) of.setColor(255, 100) of.rect(posXY, posXY, lado, lado) -- cuadrado gris -- referencia: centro -- cuadrado blanco transparente end Retomamos el ejemplo que sirvió de base para explicar la estructura. El primer bloque de comentario da datos de lo que plantea el ejemplo y la versión del software que se ha utilizado. Cuando pasa el tiempo este dato cobra importancia porque el software suele actualizarse y con ello puede cambiar el código, por lo que nos da una referencia. Hay dos variables globales, la primera actúa por dos para situar la coordenada de las formas, y es así porque en ambos cuadrados el valor de la coordenada x e y va a ser el mismo (300, 300), siguiendo Cuando se utiliza una función que define atributos de las formas, se aplica a todas formas (funciones de las formas) que están situadas debajo y en las que puede incidir, si el color va a ser el mismo en todas, solo se pone una vez al principio. Aunque una función de dibujo sólo se utilice una vez, debe ponerse en el bloque draw(), en otros software como Processing podrían ponerse en el setup() pero en GAmuza las funciones of.noFill(), of.fill() o of.enableLineWidth() tienen que ponerse en el draw() El sistema lee el código de arriba a abajo, esto afecta al orden de superposición de las formas: lo último escrito es lo que se sitúa al frente y las formas de las primeras líneas de código quedan más al fondo. En el ejemplo, el código del cuadrado blanco está escrito después que el del gris, y por tanto se le superpone, aunque la transparencia disuelve el gradiente de profundidad por superposición y crea visualmente otro cuadrado. En el siguiente ejemplo se usa transparencia, las variables de sistema de GAmuza OUTPUT_W y OUTPUT_H y se utilizan expresiones aritméticas (ver página 91) para modificar su valor. una diagonal de 45º. La segunda variable indica el lado del cuadrado. Si queremos modificar el tamaño, manteniendo las proporciones de la imagen, simplemente se cambia el valor de esas variables. 82 83 /* GAmuza 0.4.1 examples --------------------Basics/circulos_alpha.ga cambia el valor de las variables con expresiones matemáticas */ function setup() poligonos concéntricos */ function setup() end of.enableSmoothing() function draw() ga.background(0.0,1.0) of.noFill() of.setLineWidth(10) function draw() ga.background(0.0, 1.0) of.setColor(255, 20) of.circle(OUTPUT_W/2, OUTPUT_H/2, 300) of.circle(OUTPUT_W/2 +30, OUTPUT_H/2 +30, 250) of.circle(OUTPUT_W/2 -30, OUTPUT_H/2 +30, 250) of.circle(OUTPUT_W/2 +60, OUTPUT_H/2 +60, 200) of.setColor(0, 255,0) of.setCircleResolution(3) of.circle (OUTPUT_W/2, OUTPUT_H/2, 100) of.circle(OUTPUT_W/2 -60, OUTPUT_H/2 +60, 200) of.circle(OUTPUT_W/2 +90, OUTPUT_H/2 +90, 150) of.circle(OUTPUT_W/2 -90, OUTPUT_H/2 +90, 150) of.circle(OUTPUT_W/2 -120, OUTPUT_H/2 +120, 100) of.circle(OUTPUT_W/2 +150, OUTPUT_H/2 +150, 50) of.circle(OUTPUT_W/2 -150, OUTPUT_H/2 +150, 50) end of.setCircleResolution(6) of.circle (OUTPUT_W/2, OUTPUT_H/2, 220) of.setColor(0,170,255) of.circle (OUTPUT_W/2, OUTPUT_H/2, 230) Las coordenadas del centro del primer círculo son (OUTPUT_W/2, OUTPUT_H/2), estos parámetros corresponden a las coordenadas del centro de la ventana de salida. Los centros de los otros círculos mantienen relaciones con ese centro, desplazándose 45º hacia abajo a ambos lados, porque sus dos coordenadas varían de forma regular y uniforme: sumando de uno a otro 30px en direccion X y 30px en of.setCircleResolution(7) of.circle (OUTPUT_W/2, OUTPUT_H/2, 260) of.setColor(0,85,255) of.circle (OUTPUT_W/2, OUTPUT_H/2, 270) dirección Y, los que se desplazan hacia la derecha, y -30px en la X y 30px en la Y los que lo hacen hacia la izquierda. En la página 94, ésta misma construcción se plantea con una estructura condicional if. Las once capas de círculos se entremezclan visualmente por la transparencia disolviendo el gradiente of.setCircleResolution(8) of.circle (OUTPUT_W/2, OUTPUT_H/2, 300) of.setColor(170,0,255) de profundidad por las superposiciones. Todos tienen el mismo color of.setColor(255, 20) definido desde el principio, color blanco con una opacidad de aproximadamente el 8% porque todos los parámetros de esta función van de 0 a 255. of.setColor(255,255,0) of.setCircleResolution(4) of.circle (OUTPUT_W/2, OUTPUT_H/2, 140) of.setColor(85,255,0) of.circle (OUTPUT_W/2, OUTPUT_H/2, 150) of.setCircleResolution(5) of.circle (OUTPUT_W/2, OUTPUT_H/2, 180) of.setColor(0,255,255) of.circle (OUTPUT_W/2, OUTPUT_H/2, 190) of.circle(OUTPUT_W/2 +120, OUTPUT_H/2 +120, 100) 84 of.enableSmoothing() of.setCircleResolution(50) end /* GAmuza 0.4.1 examples --------------------Basics/poligonos_circle.ga of.circle (OUTPUT_W/2, OUTPUT_H/2, 310) end 85 3.2.1. Formas irregulares El bloque function setup() solo se lee una vez, no puede contener of.setCircleResolution() como en el ejemplo anterior porque en el bloque draw() se va a utilizar múltiples veces con valores distintos. La función of.setColor() tiene 3 parámetros, lo que indica que está declarada en RGB y sus valores van de 0 a 255. Para establecer las relaciones de color que siguen el círculo cromático, se han combinado dividiendo el valor máximo de cada componente del color en tres, 255/3 = 87, y haciendo También se pueden generar formas irregulares lineales o curvas. Las formas lineales requieren las funciones: combinaciones de dos componentes que incrementan o reducen su porcentaje en ese valor. Como en el ejemplo anterior, se observa que entre los parámetros hay muchos valores repetidos y otros que mantienen relaciones de crecimiento gradual, esto indica que se puede plantear de otra forma más sencilla, tal como está formulado en la página 99. of.beginShape() -- no lleva parámetros of.vertex(x, y) -- coordenadas x, y del vértice of.endShape(true o false) -- booleano para cerrar, o no, la forma of.beginShape()se utiliza como aviso de que se va a iniciar una nueva forma, esta forma estará definida por una serie de vértives con of.vertex(x, y) y tras ellos debe terminar con la función of.endShape(). Su variable booleana indica si la forma se cierra, es decir si el último vertex se une al primero, cuando es true, o si queda abierta cuando es false. /* GAmuza 0.4.1 examples --------------------Basics/begin_end_shape.ga dibujo de formas lineales irregulares */ function setup() of.enableSmoothing() end function draw() ga.background(0.0,1.0) of.noFill() of.setLineWidth(3) of.setColor(255) of.beginShape() of.vertex(146, 75) of.vertex(210, 117) of.vertex(210, 589) of.vertex(226, 589) of.vertex(190, 217) of.vertex(254, 132) of.vertex(310, 117) of.endShape(false) -- aviso empieza forma -- coordenadas de los vértices -- fin forma: abierta end 86 87 Para las formas no lineales hay distintas opciones, curvas o bezier, y en ambos tipos la posibiliad de contrucción con vértices: Las funciones of.curve() y of.bezier()no requieren señalar que se inicia y finaliza una forma. En ambas, sus 8 párametros indican dos coordenadas de principio y fin de las curvas (x1, y1, x2, y2) of.curve(cx1, cy1, x1, y1, x2, y2, cx2, cy2) of.curveVertex(x, y) of.bezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2) of.bezierVertex(cx1, cy1, cx2, cy2, x2, y2) y dos coordenadas de puntos de control (cx1, cy1, cx2, cy2), desde los que se genera la curva o el trazado de la bezier. El orden de estos parámetros no es el mismo en las dos funciones. Veamos un ejemplo con las mismas coordenadas en ambas funciones para obserbar sus diferencias: La función of.curveVertex(x,y) también necesita señalar el principio y fin de la forma, y lo hace /* GAmuza 0.4.1 examples --------------------Basics/curve_bezier.ga curveVertex(x,y) porque en la primera y la última, los parámetros (x,y) corresponden a los puntos */ con las mismas funciones: ofBeginShape() y ofEndShape(). Deben utilizarse al menos cuatro of. de control, siendo los parámetros de las otras funciones los que definen los vértices de la curva, El siguiente ejemplo compara la función of.curveVertex() con el anterior de las formas lineales con of.vertex(), desplazando el valor de las coordenadas x, además se repiten la primera y ultima función por ser las de control. comparación of.curve y of.bezier function setup() end function draw() ga.background(0.0,1.0) of.setColor(255) /* GAmuza 0.4.1 examples --------------------Basics/curveVrtex.ga comparación of.noFill() -- representan los puntos de control of.vertex y of.curveVertex */ // pegad aquí ejemplo anterior of.circle(27, 254, 4) -- para ver donde está punto control 1 (cx1, cy1) of.circle(115, 350, 4) -- y dónde punto de control 2 (cx2, cy2) of.line(27, 254, 190, 210) -- representa linea de control 1 a inicio curva of.line(115, 350, 300, 370) -- representa linea de control 2 a fin curva of.setLineWidth(3) -- grosor línea -- of.curve(cx1, cy1, x1, y1, x2, y2, cx2, cy2) // sin el end final para ver orden coordenadas of.curve(25, 254, 190, 210, 300, 370, 115, 350) -- curva color blanco of.setColor(0, 0, 200) of.curveVertex(446, 75) of.curveVertex(510, 117) of.curveVertex(510, 589) of.curveVertex(490, 217) of.curveVertex(554, 132) of.curveVertex(610, 117) of.curveVertex(610, 117) of.endShape(false) end 88 of.curveVertex(446, 75) -- of.bezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2) of.beginShape() -- define punto de control 1: no se dibuja -- inicia la curva para ver orden coordenadas of.bezier(190, 210, 25, 254, 115, 350, 300, 370) -- bezier color azul end Por último, la función of.bezierVertex(cx1, cy1, cx2, cy2, x2, y2) como tiene sólo 6 parámetros, debe que estar precedida siempre por of.vertex(x,y) cuyas coordenadas indican en -- termina la curva -- define punto de control 2: no se dibuja -- fin forma: no cierra inicio de la curva. Los parámetros (cx1, cy1, cx2, cy2,) de of.bezierVertex() posicionan los dos puntos de control, y (x2, y2) el final de la curva. Si se encadenan varias funciones seguidas, esta última coordenada indica dónde empezará la siguiente. También deben situarse entre ofBeginShape() y ofEndShape(). 89 /* GAmuza 0.4.1 examples --------------------Basics/vertex_bezier.ga 3.3. Estructuras de programación básicas 3.3.1. Expresiones aritméticas varias curvas bezier encadenadas usando vertexBezier */ Hemos visto que las cualidades visuales de una forma en la pantalla se expresan numéricamente, por function setup() pueden llegar a ser complejas para aquellos que disfruten con el campo de las matemáticas o simples lo que sus relaciones de proporción pueden derivar de operaciones aritméticas. Estas operaciones para programadores más visuales. En cualquier caso son un instrumento útil para la composición of.setCurveResolution(60) de formas, colores o sonidos, combinando variables con operadores aritméticos. Los operadores end aritméticos básicos en GAmuza provienen del lenguaje Lua, y son: function draw() ga.background(0.0,1.0) of.noFill() of.setLineWidth(3) of.setColor(255) of.beginShape() of.vertex(60, 120) --inicia curva 1 of.bezierVertex(80, -20, 280, 20, 160, 140) of.bezierVertex(10, 280, 180, 420, 280, 280) of.bezierVertex(350, 175, 440, 230, 390, 330) of.endShape(false) end calcula si un número es proporcional a otro, modular, es decir si cuando el número a la izquierda de números múltiplos de 2, 3, 4,… Ver su utilización en el ejemplo for + if de la página 97. Es común utilizar expresiones matemáticas para modificar el valor de las variables o para expresar control 1, control 2, fin curva 1 que es inicio de la 2 La función de los operadores +, -, *, / y ^ son conocidos por todos, el operador % es menos usual, % se divide por el que está a su derecha el resto es igual a cero. Es útil cuando se quiere trabajar con -- + (suma), - (resta), * (multiplicación), / (división), ^ (exponencial), %(modular) -- curva 2 -- curva 3 -- fin forma: abierta los parámetros de una función. Cuando la expresión aritmética se sitúa en el bloque update(), en cada vuelta del loop aplica de nuevo la actualización del valor, incrementándolo o reduciéndolo sucesivamente, por lo que puede utilizarse para animar formas, hasta que desaparecan de la ventana. Las expresiones condicionales regulan estos límites, tal como se muestra en la página 95. /* GAmuza 0.4.1 examples --------------------Basics/aritmetica_anima.ga */ y = 20 function setup() end function update() y = y+20 -- suma 20 cada frame end function draw() ga.background (0.0, 0.3) -- fondo transparente, se ve el movimiento de las líneas of.setColor(255) of.setLineWidth(4) of.line(0, y, OUTPUT_W, y) end 90 91 3.3.2. Expresiones relacionales la condición if, pasa a evaluar la condición Sintaxis > (mayor que) < (menor que) == (igualdad) != (desigualdad) >= (mayor o igual a) true La expresión elseif, amplía aun más las opciones de la condicional. Si no se cumple <= (menor o igual a) false Una expresión relacional está compuesta por dos valores que se comparan con un operador relacional. La expresión relacional evalúa los datos como verdadero o falso (true y false). Expresión Resultado 12 > 8 true 60 < 40 false La expresión de igualdad se hace con ==, porque el signo = aislado, significa la asignación de un valor, como en x = 14. elseif, y si tampoco se cumple puede aun establecer un else para efectuar una tercera instrucción si las dos anteriores no se cumplen. Las expresiones condicionales utilizan operadores lógicos para combinar dos o más expresiones relacionales y para invertir los valores lógicos. Sintaxis Operadores lógicos Los operadores lógicos permiten formular más de una condición de forma simultánea. El operador not, invierte el valor lógico de las variables booleanas, cambiando verdadero a falso, y falso a verdadero. La estructura condicional if en GAmuza, a diferencia de otros software, no utiliza () ni {}. if not b then end -- end, cierra el bloque if if b == true then -- si b es verdadero, entonces if a > 5 or b < 30 then -- si a es mayor que 5 ó b es menor que 30 end if a < 5 and b > 30 then --si a es mayor que 5 y b es menor que 30 end 3.3.3. Expresiones condicionales y operadores lógicos Condicionales Sintaxis if, elseif, else, (si, sino si, sino) La expresión condicional if, permite a un programa seguir comportamientos diferentes, dependiendo de que los valores de sus variables cumplan unas condiciones u otras, esta apreciación se obtiene a través de una expresión relacional (test), de modo que las acciones pueden tener lugar, o no, sólo si una condición específica se cumple: si la condición se cumple (si el test tiene como resultado true), realiza la instrucción, sino no. Para plantear el test se utilizan las expresiones relacionales y también se pueden utilizar if, de manera que cuando la expresión asociada es falsa, el código del bloque else se ejecuta en su lugar. if b -- si b es falso, entonces -- haz tal cosa then instrucción -- puede ponderse también así, si b instrucción --solo hace falta que cumpla una de las condiciones -- hace falta que se cumplan las dos condiciones La estructura if con operadores lógicos más las opciones elseif y else amplían mucho la capacidad del código en situaciones en que las variables son cambiantes . if a == b then instrucción 1 elseif a ~= c then instrucción 2 else end 92 instrucción los operadores lógicos. La expresión else amplía las posibilidades de la estructura or, and, not (o, y, no) instrucción 3 -- si a es igual a b, entonces -- haz instruccion 1 -- sino, pero si a es distinto de c -- haz instruccion 2 -- si no se cumple ninguna condición anterior -- haz instruccion 3 93 Retomemos el ejemplo de los círculos transparentes de la pág 84, aplicando la estructura if más else código. Para dibujar las capas de círculos solo hemos tenido que poner en el draw() una función para /* GAmuza 0.4.1 examples --------------------- valores de las variables incrementan si se cumple la doble condición de if planteada con el operador los que van hacia la derecha y otra para los que lo hacen hacia la izquierda. En el bloque update(), los lógico and sólo si el radio es mayor o igual a 50 y menor o igual a 300 el valor de las variables se Basics/circulos_if.ga modificarán con las expresiones aritméticas que contiene. Si no se cumplen esas dos condiciones se */ aplicará el código que contiene else. Como se ha mencionado antes, las expresiones condicionales se utilizan con frecuencia para controlar radio = 300 animaciones, de modo que reasignan nuevos valores de las variables cuando las formas alcanzan los x1 = OUTPUT_W/2 límites de la ventana de salida, generando un loop en la animación. Retomamos el ejemplo de la pág x2 = OUTPUT_W/2 91, aplicando control a esos límites, cuando la línea llega al final de la pantalla, su coordenada y vuelve y = OUTPUT_H/2 a valer 0. En la pág 130, una expresión if en vez de reiniciar el valor a 0, invierte su dirección. inc = 30 radioDec = 50 /* GAmuza 0.4.1 examples function setup() --------------------- of.setCircleResolution(60) Basics/aritmetica_anima_if.ga end Cómo controlar del movimiento con expresiones ondicionales function update() if radio >=50 and radio <=300 then -- tienen que cumplirse las 2 condiciones x1 = x1 + inc x2 = x2 - inc y = y + inc radio = radio - radioDec inc = 20 function setup() else x1 = x1-inc x2 = x2+inc y = y - inc radio = radio + radioDec end function update() of.setColor(255, 50) of.circle(x1, y, radio) end of.circle(x2, y, radio) if y < OUTPUT_H else end function draw() -- ga.background(0.0, 0.1) end */ y = 0 end 94 Con la estructura if se perciben mejor las relaciones entre las formas y se reduce la repetición de en el bloque update() se comenta el fondo -- dibuja los círculos con los datos de update end y = y + inc then y= 0 function draw() ga.background(0.0,0.2) of.setColor(255) of.setLineWidth(4) of.line(0, y, OUTPUT_W, y) end 95 3.3.4. Expresiones de iteración. Repetición es decir, recorre el eje de las X situando un punto cada 50px. for, while, repeat Las estructuras iterativas se utilizan para compactar líneas de código repetitivo, especialmente cuando que tienen un cambio modular que será aplicado a través de una variable. La estructura for se articula con tres declaraciones: valor inicial de la variable, el test con una expresión relacional y la actualización del valor de la variable (update). Las declaraciones dentro del bloque for se ejecutan de forma continua, generando un loop dentro de ese bloque mientras el test sea verdadero, true. El valor inicial de una variable se verifica en el test, la actualización modifica su valor después de cada iteración del bloque y la aplica a la instrucción. Cuando el test tiene como resultado false, el loop termina y el sistema continúa con las líneas de código que estén después del bloque for. Cuando la actualización del valor es sumar 1, suelen ponerse solo dos declaraciones: valor inicial variable y test, asumiendo la estructura que la actualización es sumar 1. Algunos ejemplos cometados. estructura for */ function setup() of.enableSmoothing() end function draw() ga.background(0.0, 1.0) of.setColor(255) of.setLineWidth(3) for i = 0, OUTPUT_W, 50 do of.line(i, 0, OUTPUT_W-i, OUTPUT_W-i) end end En el segundo par de coordenadas de las líneas, x e y tienen el mismo valor, comúnmente y suele estar vinculada a la constante OUTPUT_H, pero en este caso vamos a considerar la ventana de salida como si fuera un cuadrado. Los puntos con sus dos coordenadas iguales se sitúan siempre en una diagonal de 45º, y cómo este valor se va reduciendo simultáneamente a como crece la x primera, lo que recorre el segundo par de coordenadas es la primera línea dibujada por el for, reproduciendo así la representación plana de un paraboloide hiperbólico. Subrayar que GAmuza no utiliza () ni {} en la estructura for, y la cierra con end. La estructura for puede combinarse con if para reajustar la condición ya establecida en el test de for. En este caso se va a utilizar el if para comprender cómo funciona el operador aritmético % que vimos en la página 91. /* GAmuza 0.4.1 examples --------------------Basics/for_if.ga estructura for más if */ function setup() end /* GAmuza 0.4.1 examples --------------------Basics/simple_for.ga 96 ventana de salida, y en cada vuelta tendrá 50 píxeles más. Con esa lista de valores se van dibujando líneas en cuyo primer par de coordenadas, x responde a ese incremento de 50px e y siempre vale 0, Sintaxis Repetición La estructura for se repetirá en loop hasta que la variable i tenga el valor OUTPUT_W, ancho de la -- no lleva () ni {} -- cierra el for con end function draw() ga.background(0.0,1.0) of.setLineWidth(3) for a = 0, OUTPUT_W, 13 do if (a % 2) == 0 then of.setColor(255) elseif (a % 5) == 0 then of.setColor(255, 0, 0) else of.setColor(0,255,0) end of.line(a, 0, a, OUTPUT_H) end end 97 Dado que los valores que a va obteniendo en el loop del for tienen que cumplir otras condiciones, se ha elegido el número 13, raro, para obtener un rango más diverso, pero siempre mantendrá relaciones, es decir, generará un pattern: 0, 13, 26, 39, 52, 65, 78, 91, 104, 117, 130, 143, 156, 169, 182, 195..., par, impar, par, impar..., el primer múltiplo de 5 el 6º y luego cada 10, es decir, el primer múltiplo de 5 ha caído en la mitad de su rango por esos límites particulares que suele provocar el número 0. De este modo el for dibuja las formas y la condicional if determina los colores. La estructura iterativa for genera repeticiones en una dimensión, si se anida un for dentro de otro for se generan repeticiones en dos dimensiones. /* GAmuza 0.4.1 examples --------------------Basics/doble_for.ga estructura for+for retículas 2D */ inc = 20 function setup() of.setCircleResolution(25) end function draw() ga.background(0.0,1.0) of.setColor(255,255,255) for i=0, OUTPUT_W, inc do for j=0 , OUTPUT_H, inc do if i % j == 0 or j % i == 0 then of.setColor(0) else of.setColor(255) end of.circle(i, j, inc/2-1) end end end En este doble for, cuando la variable i adopta el primer valor, 0, el sistema pasa a asignar el primer valor a j=0, luego j=20, j=40,.. hasta que j llega al valor de la altura de la ventana, con ello ha generado las coordenadas (0,0) (0,20) (0,40)… …(0,OUTPUT_H). Después i adquiere el valor 20 y de nuevo pasa a asignar valores a j desde 0 hasta el valor de la altura de ventana, generando las 98 coordenadas de la segunda columna de círculos (20,0) (20,20) (20,40)… Así hasta que completa toda la tabla cuando i alcanza el valor del ancho de ventana y j el del alto:(OUTPUT_W, OUTPUT_H). El siguiente ejemplo retoma el planteamiento de los polígonos concéntricos y lo formula con una estructura for y el uso de color HSB /* GAmuza 0.4.1 examples ------------------Basics/for_poligonos.ga */ radio = 100 radioMax = 250 radioInc= 30 posX = OUTPUT_W/2 posY = OUTPUT_H/2 doble = 10 lados = 3 c = of.color() colorInicio = 0 colorInc = 40 function setup() of.enableSmoothing() end function draw() ga.background(0.0,1.0) of.noFill() of.setLineWidth(10) lados = 3 j = 0 for i=0, 6 do of.setCircleResolution(lados) c:setHsb((j/8)*255,255,255) of.setColor(c.r,c.g,c.b) of.circle(posX, posY, radio+(i*40)) j = j+1 c:setHsb((j/8)*255,255,255) of.setColor(c.r,c.g,c.b) of.circle(posX, posY, radio+(i*40) + 9) lados = lados + 1 end end 99 3.4. Color Solo se ponen de acuerdo en la relación entre complementarios u opuestos ¿Porqué no se puede imaginar un vidrio blanco transparente —inclusive si en la realidad no lo Complementarios 44 Cian El color es una sensación perceptiva cuyo estudio recae en muy distintas disciplinas: psicología de la Magenta Amarillo hay?¿En dónde se pierde la analogía con el vidrio transparente coloreado? percepción, física-óptica, fisiología, neurología, filosofía…. Según el enfoque de la disciplina, se le considera como un fenómeno visual vinculado a la luz y Rojo Verde Azul localizado físicamente en una porción del espectro electromagnético (espectro lumínico), o como sinestesia ente el sujeto que percibe y el objeto percibido dentro de la psicología, o como un proceso neuro-fisiológico complejo. Los ordenadores utilizan color-luz porque los materiales de las pantalla o video proyección fosforecen Como fenómeno que depende de la luz, el color recoge de ella sus paradojas, llegando a ser un claro de sistematizar las relaciones que se producen entre los componentes o parámetros del color bajo un ejemplo para mostrar la contradicción entre el mundo tangible y el intangible. Así, todas las relaciones que se establecen entre los componentes del color pigmento (materia tangible) y en el color-luz con la mezcla aditiva. Pero existen distintos modelos o espacios de color; un modelo es el resultado determinado criterio. Los tres parámetros del color: tono, saturación y luminosidad, están relacionados de manera (materia intangible) son contrarias. distinta en cada modelo, incluso utilizan nombres distintos para esos parámetros. El tono a veces lo Color-pigmento Mezcla sustractiva La mezcla de sus primarios es negro Colores primarios Magenta, Amarillo, Cian Colores secundarios Rojo, Verde, Azul encontraremos bajo la denominación de color o matiz; y la luminosidad como valor, brillo o intensidad. Color-luz Mezcla aditiva La mezcla de sus primarios es blanco El término saturación suele permanecer bastante estable en los distintos modelos. Los modelos más habituales en programación son: RGB, HSB (o HSV) y hexagesimal (principalmente vinculado a la web). Colores primarios Rojo, Verde, Azul Colores secundarios Cian, Magenta, Amarillo Magenta + Amarillo = Rojo Rojo + Azul = Magenta Amarillo + Cian = Verde Rojo + Verde = Amarillo Cian + Magenta = Azul Verde + Azul = Cian MODELO RGB El modelo RGB se basa en los colores-luz primarios (Red, Green, Blue - rojo, verde, azul) más otro canal denominado alpha que se ocupa de la transparencia. Cada dato de color RGB+alpha tiene 32 bits, 8 bits por canal y cada uno de estos canales tiene un rango de valores que va de 0 a 255, siendo el valor (0,0,0) el negro, y el valor (255, 255, 255) el blanco. MODELO HSB El modelo HSB hace referencia a los parámetros (Hue, Saturation, Brightness – Matiz, Saturación, Brillo), es muy semejante al modelo HSV 45, donde V significa Value (valor) pero en ambos casos, brillo y valor es lo mismo que hemos denominado luminosidad. El tono o matiz recorre el círculo cromático graduándose de 0 a 360º, mientras que los valores de saturación y luminosidad se scalan entre 0 y 100%. 44 Ludwig Wittgenstein (1994) Observación sobre los colores. Barcelona:Paidós, pág. 175. 100 45 El esquema del modelo de color HSV ya se mencionó en la interface de tracking video (ver página 41) 101 En openFrameworks y en GAmuza se puede trabajar con el modelo HSB pero con el rango de sus valores igual al que utiliza el modelo RGB (todos los parámetros van de 0 a 255) con la ventaja de mantener la lógica de este modelo que se adecúa mejor en trabajos con graduaciones de escalas tímbricas de color. En el caso del tono (Hue) hay que escalar los 360º a 256 valores. En la siguiente imagen se clarifica este cambio, desplegado el círculo linealmente y con la conversión de valores que se utilizan en GAmuza para los colores-luz primarios, secundarios e intermedios. La impresión cromática que se ve es orientativa pues la conversión del modo de color a CMYK, para la edición del texto, altera los tonos. Colores-luz secundarios. En RGBs se obtienen incrementando valores iguales en solo dos componentes y dejando el tercero a 0. En HSB, buscar el tono con el slider H (hue) R=255, G=0, B= 255 Magenta máxima luminosidad y saturación Pérdida de luminosidad color secundario: Si los valores son R=100, G=0, B=100 veremos un magenta oscurecido, según vayan bajando los valores irá tendiendo a negro. En HSB, bajar hacia el valor 0 el slider B. El valor 255 y 0 corresponden al mismo color Rojo, punto en el que se cerraría el círculo cromático. Pérdida de saturación color secundario La herramienta Color Selector del ScriptEditor, ayuda a calcular los valores y ver las relaciones entre los Se desaturan incrementando el valor de ese tercer componente. dos modelos, porque al mover los sliders de uno se adjustan también los del otro. Los colores luz primarios en RGB tienen el máximo valor en su parámetro correspondiente y 0 en los otros dos. R=0, G=0, B=255 Azul con máxima luminosidad y saturación. En HSB, simplemente dsplazar el primer slider (Hue) hasta percibir el color. Pérdida de luminosidad colores primarios: Si los valores son R = 0, G=0, B=128, veremos un azul oscuro por la pérdida de luminosidad al 50%. Según va bajando el valor del azul hacia 0, y los otros permanecen igual, se llega al negro. En HSB, ir bajando el slider B hacia el 0 En el ejemplo anterior, R = 255, G=170, B= 255, es un magenta desaturado, si los tres alcanzan 255, llegan a blanco. En HSB, bajar hacia el valor 0 el slider S. Los colores terciarios surgen al mezclar en la misma proporción un primario y un secundario que no lo contiene, es decir, con su color complementario al 50%. En este nivel surge el mundo de los marrones, o tierras, dejando las escalas tímbricas de colores más puros para entrar en las escalas tonales46. Para traducir estas relaciones a valores RGB, los colores terciarios sin desaturar y sin pérdida de luminosidad, se obtienen combinando en proporciones distintas dos componentes del color, dejando el tercero con un valor 0, o muy diferenciado de los otros. En HSB son tonos que siempre tienen un descenso de luminosidad. Para desaturarlos en RGB se incrementa proporcionalmente el valor de los tres componentes. En HSB se baja el valor de S. Pérdida de saturación colores primarios: Volvemos al valor B = 255, conforme G y B van incrementando sus valores de forma equivalente, el rojo se irá desaturando. Cuando todos alcancen el valor 255 llegarán al blanco. En HSB, ir bajando el slider S hacia el 0 102 46 Gillo Dorfles, (1963) El devenir de las artes, México: Fondo de Cultura Económica, pág. 90 Con esta distinción Dorfles aproximó los términos del color a los de la música.. 103 Para reducir la luminosidad se disminuye proporcionalmente el valor de los dos componentes iniciales. En HSB, bajar el valor de B. 3.5. Azar y probabilidad Una tirada de dados jamás abolirá el azar Sthéphane Mallarmé Si en RGB se mantienen los tres valores semejantes pero no iguales, se obtienen colores grisaceos con dominate del que tiene, o los dos que tienen, el valor superior, al incrementarlos proporcionalmente se desaturan y al reducirlos van perdiendo luminosidad. El paso de la física determinista a la física de probabilidades implicó un cambio de pensamiento que ha repercutido más allá de la ciencia. Las relaciones entre orden, caos y aleatoriedad influyeron fuertemente en el mundo del arte, llegando a ser un motor de creación artística para las propuestas En Gamuza, el modelo de color HSB se utiliza a través de la clase de openFrameworks of.color(). El proceso es el siguiente: experimentales de los años 60 y 70, como las composiciones musicales de John Cage, por ejemplo, quien definió el proceso de creación de la pieza Willian Mix (1953) del siguiente modo: A través de medios elaborados al azar, determiné previamente cuáles de aquellas categorías tenían que agruparse, y qué parámetros había que modificar. Se llamó a las categorías A, B, C, D, F; y la Se vincula una variable global con la clase of.color() Y en el bloque draw() esa variable establece el puente o traducción de un sistema a otro a través del método setHsb() agrupación de estos elementos venía determinada por operaciones al azar del I Ching47. nombreVariable = of.color() nombreVariable:setHsb(valor1, valor2, valor3) -- violeta sería(192,255,255) Después se pone la función habitual para definir el color, pero en sus parámetros se vincula la variable Esta descripción bien parece un esquema de programación, y como todos los procesos aleatorios, tiene como particularidad producir formas o sonidos que no pueden conocerse de antemano, pero dentro el proceso no está dejado al azar en su totalidad, sino que marca un esquema dentro del cual el azar establece las relaciones. Azar y aleatoriedad permiten observar cómo la incertidumbre entra en el mundo de las matemáticas, a .r, .g y .b: donde la definición de aleatoriedad se expresa como todo es aquello que bajo el mismo conjunto efecto. Las matemáticas han desarrollado la teoría de la probabilidad y los porcesos estocásticos para of.setColor(nombreVariable.r, nombreVariable.g, nombreVariable.b) En los ejemplos de las páginas 86 y 99 se puede comparar el uso de los dos modelos. aparente de condiciones iniciales, puede presentar resultados diferentes, rompiendo el patrón causacaracterizar las situaciones aleatorias. En programación, todos los lenguajes contemplan funciones random para representar estas situaciones. En GAmuza, se puede usar la función of.random(valor 1, valor2) entre el rango que establecen esos dos valores, el sistema eligirá uno al azar. Esta función suele utilizarse para generar formas o sonidos menos definidos y que presentan una configuración distinta cada vez que se activan, pudiendo reflejar un carácter orgánico. En el siguiente ejemplo, un número determinado de puntos se distribuyen siguiendo una diagonal de 45º de forma uniforme pero fluctúan a un lado y otro de esa diagonal con una doble aleatoriedad que va incrementando sucesivamente el rango de fluctuación. 47 Richard Kostelanetz (1973), Entrevista a John Cage, Barcelona: Anagrama, pág. 36. 104 105 /* GAmuza 0.4.1 examples --------------------Basics/random_diagonal.ga diagonal de puntos que fluctúan of.random(), cuyos valores están a su vez sujetos a la indeterminación de otra función of.random() que tiende a incrementarlos. Aunque hagamos un random de random recursivo, la función of.random() produce lo que se conoce como “una distribución «uniforme» de números” incrementando el random Los números aleatorios que recibimos de la función random() no son verdaderamente aleatorios y por lo tanto, se conoce como “pseudo-aleatorios”. Son el resultado de una función matemática que simula el azar. Esta función daría lugar a un patrón durante un tiempo, pero ese período de tiempo es tan largo que para nosotros, parece tan bueno como el azar puro!48 */ totalPuntos = 800 rango=0 La conjunción de azar y probabilidad busca producir una distribución «no uniforme» de números function setup() mismas condiciones. function draw() Pensemos de nuevo en la descripción de John Cage. Para trabajar con el azar Cage establece 6 ga.background(0.0,1.0) of.setColor(255) rango = 0 for i=1, totalPuntos do for j = 1, totalPuntos do if i == j then of.circle(i + of.random(-rango, rango), j+ of.random(-rango, rango), 1) rango = rango+ of.random(-1,1.5) end end end end La función for tiene sólo dos declaraciones: valor inicial de la variable y test (hasta que i sea menor o igual a totalPuntos); al no tener la tercera se entiende que es 1, por lo que su acción se reduce a recorrer todos los valores entre 1 y 800. Cada vez que pasa de un valor a otro, el otro for recorre sus 800 pasos y vuelve el primer for a sumar un punto más... como se vio en la página 96. categorías para los sonidos: A (sonidos de la ciudad), B (sonidos del campo), C (sonidos electrónicos), D (sonidos producidos de forma manual), E (sonidos producidos por el viento) y F (“pequeños” sonidos que deben ser amplificados), y además establece en todos ellos otros parámetros vinculados al tono, timbre e intensidad. Después tira las monedas del I Ching para que el azar decida la composición a partir de ese diagrama de relaciones. No se trata de una aleatoriedad uniforme entre todos los sonidos, sino que opera articulándose con un contexto de relaciones condicionales. Pero incluso las decisiones que se puedan tomar siguiendo el ejemplo más sencillo del azar, tirar una moneda al aire, tiene condiciones. En principio existe una equiprobabilidad, es igual de probable que salga cara o cruz: 1/2 = 50% Lanzar la moneda una sola vez, nos sitúa ante eventos independientes, cuyo resultado no está condicionado ni tiene efecto en la probabilidad de que ocurra cualquier otro evento. Pero si lanzamos la moneda dos veces, la probabilidad de que salga cara en ambos sucesos cambia. Según los principios básicos de probabilidad, que 2 o más eventos independientes ocurran juntos o en sucesión, es igual al producto de sus probabilidades marginales: 1/2 * 1/2 = 1/4 el 25% De todos esos valores, la condicional if desestima las opciones en los valores del primer y segundo En el caso de tres tiradas (..., el I Ching implica 6 tiradas de 3 monedas cada vez): siguiendo una línea de 45º. En la función interna of.circle(), las coordenadas x e y alteran esa distribución uniforme al fluctuar o no, otro evento. for no son iguales, por lo que al aplicar los datos que pasan el filtro como coordenadas, se sitúan un número indeterminado de píxeles a un lado y otro de esa dirección como consecuencia de la función 106 aleatorios, reflejando el hecho de en ningún sistema todos los elementos son iguales, ni tienen las end 1/2*1/2*1/2 = 1/8 el 12’5%. Así llegamos a que la probabilidad de que suceda un evento está condicionada a que haya ocurrido, 48 Daniel Shiffman, Nature of code [draft 5.4.2012] pág. 6. 107 Daniel Shiffman propone otro ejemplo particular dentro del campo del azar, el Camino aleatorio, Este caminante recorre 4 px en cada paso, lo que transforma la opción de puntos en pequeñas líneas que de algún modo mantiene también relación con las acciones artísticas psicogeográficas de los endacenadas. El segundo par de coordenadas (xsig, ysig) de cada línea será el primero de la siguiente, trazando así el recorrido de su deriva. Situacionistas. Camino aleatorio (Random Walks) es una formalización matemática de la trayectoria que resulta de hacer sucesivos pasos aleatorios. Por ejemplo, la ruta trazada por una molécula mientras viaja por un líquido o un gas, el camino que sigue un animal en su búsqueda de comida, el precio de una acción fluctuante y la situación financiera de un jugador pueden tratarse como un camino aleatorio. El término Camino aleatorio fue introducido por Karl Pearson en 1905. Los resultados del análisis del paseo aleatorio han sido aplicados a muchos campos como la computación, la física, la química, la ecología, la biología, la psicología o la economía49. El siguiente ejemplo muestra la programación de un Camino aleatorio tradicional que se inicia en el centro de la pantalla y a cada paso elige al azar una entre Como el fondo en GAmuza se dibuja de nuevo en cada frame, para visualizar el recorrido hay que quitar o comentar la función ga.background(). En esta opción tradicional existe 1/8 = 12,5% de probabilidades de que el punto se traslade a una las 8 posiciones posibles cada vez que se efectúa el random, indistintamente de cuantas veces lo haga. Daniel Shiffman50 plantea un Random Walker que tiende a moverse hacia una dirección porque, como mencionaba la defición de este algoritmo, su planteamiento sirve para representar trayectorias, especialmente en medios fluidos, como el vuelo de las aves, o el movimiento en líquidos o gases, las 8 opciones, que en coordenadas oscilará entre +1 y -1 tanto para x como donde la rigidez de la geometría euclideana queda relegada por los flujos o turbulencias, pero más allá of.randomf(), sin parámetros, porque son esos los que están ya establecidos están totalmente abiertas sino que marcan una tendencia de dirección. Shiffman regula esta dirección para y. Por eso, en lugar de utilizar la función of.random(-1, 1) se usa de la deriva como acción artística que recurre al azar indeterminado, esas trayectorias orgánicas no por defecto. de trayectoria por medio de porcentajes, planteando un Random Walker que tiende a la derecha con las siguiente probabilidades: /* GAmuza 0.4.1 examples --------------------Basics/random_Walker01.ga Random Walker tradicional */ Quedando el código de programación sujeto a condicionales if. /* GAmuza 0.4.1 examples --------------------- x0 = OUTPUT_W/2 Basics/random_Walker02.ga y0 = OUTPUT_H/2 Random Walker condicionado step = 4 */ function draw() -- ga.background(0.0,1.0) of.setColor(255) xsig = x0 + (step * of.randomf()) ysig = y0 + (step * of.randomf()) of.line(x0, y0, xsig, ysig) x0 = xsig -- el valor de las dos ultimas coordenadas de la línea y0 = ysig -- pasa a las primeras para unir las líneas en recorrido end 49 Definición Camino aleatorio [enciclopedia on-line] <http://es.wikipedia.org/wiki/Camino_aleatorio> [31.07.2012] 108 Moverse hacia arriba 20%, abajo 20%, izquierda 20%, derecha 40%. x0 = OUTPUT_W/2 y0 = OUTPUT_H/2 step = 4 xsig = x0-step ysig = y0-step function update() x0 = xsig y0 = ysig end 50 Daniel Shiffman, Nature of code [draft 5.4.2012] pág. 8. 109 function draw() --ga.background(0.0,1.0) el fondo se ha anulado of.setColor(255) r = of.random(1) if r < 0.4 then -- 40% hacia derecha xsig = x0 + step elseif r < 0.6 then -- si solo hay un parámetro es entre 0 y ese valor -- no es 60%, elseif solo actúa si no cabe en if xsig =x0 - step elseif r < 0.8 then ysig = y0 + step else /* GAmuza 0.4.1 examples --------------------Basics/noise01.ga modulación del movimiento ysig = y0 - step */ end v=0 -- parámetro noise of.line(x0, y0, xsig, ysig) inc = 0.1 -- incremento end function setup() Sin hacer nunca el mismo camino, siempre que activemos el programa, el recorrido va a la derecha. end function draw() ga.background(0.0,1.0) of.setColor(255) of.noFill() 3.5.1. Noise de la película Tron. El Ruido Perlin es una función matemática que utiliza interpolación entre un gran número de gradientes precalculados de vectores que construyen un valor que varía pseudo-aleatoriamente en el espacio o tiempo. Se parece al ruido blanco, y es frecuentemente utilizado en imágenes generadas por computadora para simular variabilidad en todo tipo de fenómenos51. La función of.noise() permite generar formas, texturas y movimientos con una apariencia más orgánica, porque interpola entre los valores random otros datos para crear transiciones más suaves que se reflejan como pequeñas irregularidades. 51 Definición de Ruido Perlin [enciclopedia on-line] < http://es.wikipedia.org/wiki/Ruido_Perlin > [01.08.2012] 110 -- n da un valor modulado para y v=v+inc uniforme, no uniforme o personalizada, pero son valores que no guardan relación entre sí. La función proviene de la función matemática Noise Perlin desarrollada por Ken Perlin para generar las texturas -- i da un valor regular para x n = of.noise(v)*100.0 of.rect(i, n, 20, 20) Con la función of.random() se pueden generar series de valores inesperados, con una distribución of.noise() genera también valores inesperados pero de una forma más controlable. Su nombre for i=0, OUTPUT_W, 20 do -- en cada loop del for el valor del parámetro de noise cambia end end La función of.noise() no genera realmente números más relacionados ente sí que la función of.random(), funciona de manera diferente. El rango de los datos de salida es fijo y siempre devuelve un valor decimal entre cero y uno. Siempre el mismo valor. Por eso, en el ejemplo, el cálculo se realiza sobre una variable v que va incrementando su valor paso a paso, v = v + inc. y se multiplica por 100 para escalarlo. Se genera una imagen en movimiento de una cadena de cuadrados que serpentean ondulatoriamente de forma continua e irregular. Otra opción es utilizar la función of.map() para mapear los datos. El rango de números decimales entre 0 y 1 de of.noise() son adaptados por of.map() a través de 6 parámetros: of.map (valor, inputMin, inputMax, outputMin, outputMax, fijarOut(true o false)) 111 3.6. Textos y tipografía El primer parámetro es el valor que queremos asignar, en el ejemplo es v (el resultado de of.noise()); el segundo y tercero corresponden al rango actual de valores mínimo y máximo obtenidos por of.noise(), que sabemos son 0 y 1. Los dos siguientes parámetros son el rango al que deseamos escalar los valores de entrada, en el siguiente ejemplo va a ir de 0 a OUTPUT_W, y el último parámetro permite fijar los resultados o no, mediante la opción boolenana true o false. Veamos un ejemplo. Entre los tipos de datos que almacena y con los que opera el software están también las palabras, no como nombres de variables sino entendidas como caracteres (char), letras, y/o cadena de caracteres (string) , palabras, pudiendo incorporar texto como imagen, pues las fuentes se convierten en texturas que se dibujan en la pantalla. En GAmuza se pueden utilizar las clases of.trueTypeFont() o ofx.trueTypeFontUC() Para ambas es necesario cargar el tipo de fuente previamente y asociar una variable global a la clase de openframeworks o de ofx. /* GAmuza 0.4.1 examples --------------------- nombreVariable = of.trueTypeFont() function setup() nombreVariable:loadFont(“fonts/Anonymous_Pro_B.ttf”,50, true, true) Basics/noise_map.ga modulación del movimiento Los métodos de vinculan a las variables con el operador “:” (clases y métodos se verán, en la página */ 136). Los parámetros del método loadFont() son: • Tipo de fuente (incluyendo la ruta donde está guardada), v = 0.0 • Tamaño, inc = 0.01 • Si tiene anti-aliasing (booleano), • Si la fuente contiene el conjunto completo de caracteres o un subconjunto —ASCII extendido, que puede incluir diéresis (aunque no acentos) o ASCII normal— también booleano. function setup() end GAmuza crea una carpeta con el mismo nombre que se le ponga al archivo, es en esa carpeta donde function update() .... v = v + inc end debe guardarse una copia de la fuente a utilizar. El contenido del texto se dibuja en el bloque draw() con el método drawString() que tiene 3 parámetros: el texto entre comillas “texto entrecomillado” y las coordenadas que definen la function draw() ga.background(0.0, 0.1) of.setColor(255) x = of.map(of.noise(v),0,1,0,OUTPUT_W, false) y = OUTPUT_H/2 + of.random(-4, 4) of.circle(x,180,16,16) posición de inicio del texto. nombreVariable: drawString(“texto entrecomillado”, x, y) Si se va a modificar el tamaño mientras el programa está activo, la función loadFont() se pone en el bloque draw() o el valor del tamaño vinculado a una variable que se actualiza en el bloque update(). end 112 113 El mundo de la programación es anglosajón, por lo que para trabajar con acentos, ñ..., hay que utilizar la clase ofx.trueTypeFontUC() para texto Unicode52 Además de los métodos loadFont() /* GAmuza 0.4.1 examples --------------------texto con acentos, tamaño y posición random texto = ofx.trueTypeFontUC() end function update() t = of.random(30, 100) -- actualiza el valor del tamaño end function draw() ga.background(0.0,0.5) -- fondo negro 50% transparencia of.setColor(255, 0, 255) -- color magenta posX = of.random(50,OUTPUT_W-50) posY = of.random(50, OUTPUT_H-50) texto:loadFont(“fonts/verdana.ttf”, t, true, true) -- tamaño variable -- kerning vinculado a tamaño texto:drawString(tx, posX ,posY) end -- espacio entre palabras ofx.arcText() -- para textos con posiciones curvas ofx.FTGLFont() -- si requiere openGL ofx.setSubtitles() -- trabaja con archivos .srt porpios de subtítulos -- llama a un archivo .txt para traer el contenido created by n3m3da | www.d3cod3.org */ font = ofx.arcText() text = “GAmuza - Hybrid Live Coding” function setup() of.setCircleResolution(50) font:loadFont(“fonts/D3Litebitmapism.ttf”,58) font:setLetterSpacing(1.3) end function draw() ga.background(0.9,1.0) end 52 “Unicode es un estándar de codificación de caracteres diseñado para facilitar el tratamiento informático, transmisión y visualización de textos de múltiples lenguajes y disciplinas técnicas, además de textos clásicos de lenguas muertas”. [enciclopedia on-line] <http://es.wikipedia.org/wiki/Unicode> [30.08.2012] 114 setEspaceSize() /* GAmuza 0.4.1 examples --------------------Font/curveText.ga dibujar texto sobre curva usando ofx.arcText()54 tx = “Randonée” -- contenido texto texto:setLetterSpacing(t/50) -- altura de la línea de texto que se dibuja en pantalla El siguiente ejemplo está accesible en File/Examples/Font/ , y utiliza la clase ofx.arcText() -- tamaño fuente function setup() setLineHeight() ofx.wordPalette() */ drawString(), en el ejemplo se ha utilizado Existen también otras calses del addon ofx vinculadas a textos y tipografía: Font/unicode_random.ga t = 30 y setLetterSpacing(), que regula el kerning (espaciado entre las letras). Otros métodos posibles son53: of.setColor(0) of.pushMatrix() of.translate(OUTPUT_W/2,OUTPUT_H/2,0) of.rotateZ(of.getElapsedTimef()) font:drawString(text,0,0,OUTPUT_H/2) of.popMatrix() 53 Más información en “ofTrueFont” [texto on-line] http://www.openframeworks.cc/documentation/graphics/ofTrueTypeFont.html [03.08.2012] 54 addon source <https://github.com/companje/ofxArcText> [01.09.2012] 115 Se pueden utilizar texto como formas con la clase of.trueTypeFont(). En el siguiente ejemplo se usan para ello las siguientes clases, métodos y funciones: • La clase de openFrameworks of.path()55: crea un path o múltiples path a través de puntos, generando un vector. Está vinculada con los métodos clear() que borra los puntos y setFilled() cuyos parámetros true o false establecen si el path es wireframes o relleno. • La función de Lua string.byte(): devuelve el código numérico interno del carácter que se ponga como parámetro • El método getCharacterAsPoints(): obtiene los puntos que definen el carácter y que serán usados por of.path() para el trazado . 56 /* GAmuza 0.4.1 examples --------------------Font/fontShapes.ga Cómo usar la clase of.trueTypeFont para dibujar texto desde .ttf o .otf como formas vectoriales // draw character as bitmap font1:drawString(“&”, 50, 250) // draw the character as shapes character:setFilled(true) character:draw(200,250) character:setFilled(false) character:draw(350,250) character:clear() // draw text as shapes of.fill() font2:drawStringAsShapes(“Hello - I am a vector”,50,480) of.noFill() font2:drawStringAsShapes(“Hello - I am a vector”,50,550) end created by n3m3da | www.d3cod3.org */ font1 = of.trueTypeFont() font2 = of.trueTypeFont() character = of.path() letter = string.byte(‘&’) function setup() font1:loadFont(“fonts/Anonymous_Pro_B.ttf”,160,true,true,true) font2:loadFont(“fonts/D3Litebitmapism.ttf”,52,true,true,true) character = font1:getCharacterAsPoints(letter) end function update() end function draw() ga.background(0.0,1.0) // draw text as bitmap of.setColor(0,90,60) font2:drawString(“hello - I am a bitmap”,50,400) 55 ofPath Reference <http://www.openframeworks.cc/documentation/graphics/ofPath.html> [01.09.2012] 56 ofTrueTypeFont Class Reference ver < http://www.undef.ch/uploads/ofDoc/html/classof_true_type_font.html > [01.09.2012] 116 117 3.7. Trigonometría La trigonometría es una rama de las matemáticas cuyo significado etimológico es “la medición de los triángulos” y sirve para definir relaciones entre los lados y ángulos de los triángulos. Básicamente es una extensión del Teorema de Pitágoras Las funciones trigonométricas y las funciones que más se utilizan son las del seno y coseno, con ellas se generan números repetitivos que pueden ser utilizados para dibujar ondas, círculos, arcos y espirales. La unidad de medida angular propia de la trigonometría es el radian, en una circunferencia completa hay 2π radianes. Para ver cómo funcionan gráficamente, retomamos un ejemplo de Ben Fry y Casey Reas58, traducido al lenguaje de programación de GAmuza, porque permite observar qué parámetros controlan la amplitud y frecuencia de las ondas con los valores del seno, math.sin(), en una estructura for. Esta unidad puede traducirse a Grado sexagesimal: unidad angular que divide una circunferencia en 360 grados; o al Grado centesimal: unidad angular que divide la circunferencia en 400 grados centesimales. OpenFrameworks cuenta con las siguientes constantes para trabajar con unidades radianes: PI TWO_PI M_TWO_PI FOUR_PI HALF_PI Para convertir grados a radianes y viceversa, pueden utilizarse las funciones de Lua math.deg() y math.rad() y también las funciones de openFrameworks of.degToRad() y of.radTodeg(). Las funciones math.sin(x) y math.cos(x) se utilizan para determinar el valor del seno y coseno de un ángulo en radianes. Ambas funciones requieren un parámetro, el ángulo. El siguiente gráfico orienta los valores del seno y coseno en función de los ángulos57. 57 Más información sobre funciones matemáticas Lua en http://lua-users.org/wiki/MathLibraryTutorial [30.07.2012] 118 /* GAmuza 0.4.1 examples --------------------Trigonometria/sin.ga formas de onda regulables */ angle = 0.0 pos = 50.0 amplitud = 35.0 inc = PI/20.0 function draw() ga.background(0.0,1.0) for x = 0, OUTPUT_W, 5 do y = pos + (math.sin(angle) *amplitud) of.setColor(255) of.rect(x, y, 2, 4) angle = angle + inc end angle = 0.0 end 58 Casey Reas y Ben Fry, (2007) Processing, a Programming Handbook for Visual Designers and Artist and. Cambridge(MA): The MIT Press, pág. 119-120. 119 En el siguiente ejemplo se utilizan valores del seno y el coseno para calcular los centros de una espiral de círculos, haciendo una conversión de grados a radianes Si en este ejemplo se modifican los valores asignados a las variables se puede observar los cambios en la frecuencia de la onda. La variable pos define la coordenada Y de la onda, la variable amplitud controla la altura de la onda, y la variable inc el incremento del ángulo. Las siguientes imágenes muestran los cambios de frecuencia al modificar esos valores y el ángulo. pos = 25 /* GAmuza 0.4.1 examples --------------------Trigonometria/sin_cos.ga grados a radianes, espiral */ radio = 30 function setup() of.enableSmoothing() pos = 75 end function draw() ga.background(0.0, 1.0) amplitud=5.0 of.setColor(255) radio = 30 for grado = 0, 360*4, 10 do amplitud=45.0 inc=PI/12.0 angle = math.rad(grado) x = UTPUT_W/2+ (math.cos(angle) * radio) y = OUTPUT_H/2+ (math.sin(angle) * radio) of.circle(x, y, 4) radio = radio + 1 end end inc=PI/90.0 La estructura for recorre cuatro veces los 360º de un círculo de 10 en 10 grados. El resultado de los grados que se obtiene (10, 20, 30, 40,...) se pasa a radianes mediante la función de Lua math.rad(), angle=HALF_PI por ejemplo math.rad(180) = 3.1415926535898, es decir PI. La espiral crece por el incremento del radio, si se comenta o quita radio=radio+1, el código dibuja un círculo de círculos. angle = PI La repetición del valor inicial de la variable, radio = 30, en el draw() y fuera del for sirve para detener el crecimiento en loop de su valor. 120 121 3.8. Transformar: traslación, rotación Una variación del este ejemplo nos lleva a la configuación de obra Rotating Disc de Marcel Duchamp. En el siguiente apartado volveremos a ella para aplicarle la rotación. De momento analizamos el código para generar la imagen fija. /* GAmuza 0.4.1 examples --------------------Trigonometria/duchamp_disc.ga */ radio = 15 spiralFactor = 15 numCircles = 23 function setup() of.enableSmoothing() of.setCircleResolution(50) end function update() Sintaxis: of.pushMatrix(), of.popMatrix() of.translate(), of.rotate() La matriz de pixeles en la que se representan las formas mediante coordenadas, puede transformarse trasladándola o girándola, es decir, no giramos y trasladamos los objetos sino la textura de salida en su conjunto. Por esta particularidad antes de trasladar o girar se deben (se recpmienda) utilizar las funciones of.pushMatrix(), y of.popMatrix() para controlar ese desplazamiento. Estas funciones siempre van juntas marcando el inicio y el final del proceso. Es como si se generara una textura paralela sobre la ventana de salida donde se aplican las funciones of.translate(), of.rotate() end function draw() ga.background(0.0, 1.0) of.setColor(255) of.noFill() of.setLineWidth(3) radio = 15 for i = 0, numCircles do angulo = i*TWO_PI/((numCircles/2) +1) x = (OUTPUT_W/2) + (math.cos(angulo) * spiralFactor) y = (OUTPUT_H/2) + (math.sin(angulo) * spiralFactor) of.circle(x, y, radio) radio = radio + spiralFactor end end La funcion of.translate() mueve la textura de salida desde su coordenada (0, 0, 0) hasta el punto que se indiquen las coordenadas of.translate(x, y, z), si se está trabajando en 2D la coordenada z es 0. Pero solo las figuras situadas después de la función se verán afectadas por la translación. En las transformaciones con rotacion, of.rotate(), suelen aplicarse tanto las funciones El centro de todos los circulos está situado en el perímetro de un círculo no dibujado, cada uno de ellos separado por 45º. La diferencia de sus radios se corresponde al valor de ese mismo circulo invisible que distribuye la tangencia de los otros, es esa tangencia lo que produce la percepción de una espiral. 122 of.pushMatrix(), y of.popMatrix()como of.translate(), que traslada el punto 0 de la pantalla al eje de rotación. Se pueden apilar tantas superposiciones de la textura de salida como funciones se pongan en el código. Retomamos el ejemplo basado en Rotating Disc de Duchamp para ver el uso de estas funciones. 123 /* GAmuza 0.4.1 examples --------------------Basics/disco_giratorio_duchamp.ga Trigonometry + rotate created by n3m3da | www.d3cod3.org */ radio = 15 spiralFactor = 15 numCircles = 23 radioMax = (radio*(numCircles+1)) + (spiralFactor+1) inc = 15 counter = 0 function setup() of.enableSmoothing() of.setCircleResolution(50) end La estructura for que dibuja los círculos está después de las funciones of.pushMatrix(), of.translate(), of.rotate()que, traducido de forma simplificada es, emula una nueva capa de textura de salida, traslada su punto cero al centro y gira un número de veces (counter) que se va incrementando cada vez que el for ha terminado (counter +1), después del for se resitúa la textura de salida y vuelve a empezar por el loop que hace el bloque draw(). Algunas veces las funciones translate() o rotate() pueden ir sin pushMatrix y popMatrix, e incluso situarse dentro de un for de modo que la translación se repita en cada paso del loop. /* GAmuza 0.4.1 examples --------------------- Basics/translate_for.ga repetición de translación en for */ function setup() of.enableSmoothing() end function draw() ga.background(0.0, 1.0) of.setColor(255) of.noFill() of.setLineWidth(3) of.circle(OUTPUT_W/2,OUTPUT_H/2, radioMax) radio = 15 of.pushMatrix() of.translate(OUTPUT_W/2,OUTPUT_H/2, 0.0) of.rotate(counter) for i = 0, numCircles do angulo = i*TWO_PI/((numCircles/2) +1) x = (math.cos(angulo) * spiralFactor) y = (math.sin(angulo) * spiralFactor) of.circle(x, y, radio) radio = radio + spiralFactor end of.popMatrix() counter = counter + 1 end 124 of.setCircleResolution(50) function draw() ga.background(1.0,1.0) of.setColor (204, 116, 5) of.noFill() radio = 0 angulo = 0 radioInc = 0.2 posy = OUTPUT_H/6 amplitud = 5 for i = 0, OUTPUT_W do of.translate(3, posy, 0.0) angulo = math.rad(i) of.circle(radio/2, radio/2, radio) posy = math.sin(angulo) * amplitud radio = radio + radioInc if i== OUTPUT_W/4 then end end radioInc = radioInc * -3 end 125 3.9. Programación orientada a objetos. POO Esta forma particular de estructurar el código favorece su reutilización al compartimentarse en módulos independientes. Recordad la cita de Katherine B. McKeithen62 respecto a la importancia de organizar bien la información para procesarla como grupos significativos, algo así sucede en la POO. La programación orientada a objetos (POO) es un modelo de programación, una forma particular de pensar y escribir el código, en la que se utilizan los elementos básicos vistos anteriormente: variables, expresiones y funciones..., pero organizados de modo más próximo al lenguaje verbal. El hecho de ser un modelo implica que tiene su propia lógica, y para usarlo no sólo debe comprenderse sino que hay que pensar el desarrollo del código según ese modelo, para poder aplicar después el lenguaje. La POO nos hace pensar las cosas de una manera distinta, más abstracta y con una estructura más Nos centraremos en dos elementos que tienen un papel importante en este juego del pensar diferente, Tablas [arrays] y Clases; señalando la particularidad de esos elementos en GAmuza, pues la hibridación de plataformas entre openFrameworks y Lua, plantean otra forma de aproximarse a la programación orientada a objetos. En la descripción de algunos ejemplos se ha mencionado ya la presencia de Clases, como of.trueTypeFont() y de sus métodos. En este apartado se explicará su funcionamiento. organizada, que sigue un proceso de descomposición del problema en partes bastante particular. Los métodos tradicionales de programación estructurada descomponen la complejidad en partes más simples y fáciles de codificar, de manera que cada sub-programa realiza una acción. Se trata de descomponer el problema en acciones, en verbos [...] por ejemplo el verbo pedir, el verbo hallar, el verbo comprobar, el verbo calcular…59 3.9.1. Tablas [arrays] En la POO no se descompone el problema en las acciones que tiene que hacer el programa, sino en objetos, por lo que debemos pensar “cuál es el escenario real del mismo, y vamos a intentar simular ese escenario en nuestro programa”60. El elemento básico de esta estructura de programación no son las funciones sino los objetos, entendidos como la representación de un concepto que contiene información necesaria (datos) para describir sus propiedades o atributos y las operaciones (métodos) que describen su comportamiento en ese escenario. Por eso implica una manera distinta de enfocar los problemas, que empieza por la abstracción, entendida en este caso como la capacidad mental para representar una realidad compleja en los elementos separados simplificados (objetos), ver aisladamente sus necesidades de definición y comportamiento, y también sus relaciones (mensajes) y comportamiento conjunto. Si nos detenemos a pensar sobre cómo se nos plantea un problema cualquiera en la realidad podremos ver que lo que hay son entidades (otros nombres que podríamos usar para describir lo que aquí llamo entidades son “agentes” u “objetos”). Estas entidades poseen un conjunto de propiedades o atributos, y un conjunto de métodos mediante los cuales muestran su comportamiento. Y no sólo eso, también podremos descubrir, a poco que nos fijemos, todo un conjunto de interrelaciones entre las entidades, guiadas por el intercambio de mensajes; las entidades del problema responden a estos mensajes mediante la ejecución de ciertas acciones.61 59 Luis R. Izquierdo (2007) “Introducción a la programación orientada a objetos”. Pág. 2 [texto on-line] http://luis.izqui.org/resources/ ProgOrientadaObjetos.pdf [25.07.2012] 60 Ibidem. 61 Ibidem. 126 El término array en programación y matemáticas está vinculado a matriz, y significa un conjunto ordenado consecutivamente de elementos, del mismo tipo de datos, almacenados bajo el mismo nombre y para los que el sistema reserva memoria (igual que para las variables, pero aquí como una serie de variables relacionadas). El orden consecutivo asigna a cada elemento un ID, este ID es una forma numérica consecutiva que empieza por 0 y lo identifica de manera que se puede acceder a ese elemento en la programación individualmente. Los datos pueden ser de cualquier tipo pero no se pueden mezclar distintos tipos en un mismo array. Hay arrays de una dimensión (lineales) o de dos dimensiones (matrices) que podemos visualizarlo como una fila de contenedores que acogen cada uno de ellos un dato. En el siguiente ejemplo vemos como un array de 20 elementos se ordena hasta la posición 19 porque empieza a contar desde 0: Los arrays de dos dimensiones constituyen una matriz de filas y columnas. Por ejemplo, la ventana de salida podemos verla como un array cuyo número de elementos es igual a los píxeles de la imagen y 62 Katherine B. McKeithen et al., “Knowledge Organization and Skill Differences in Computer Programmers” Cognitive Psychology 13(3) Pág. 307. [Texto on-line] [Consultado: 12/05/2012] http://hdl.handle.net/2027.42/24336 127 function draw() ga.background(0.0,1.0) of.setColor(255,0,0) -- color rojo of.noFill() -- no rellenar for i = 0, 100 do -- dibuja los 100 círculos of.circle(myTable[i].x, myTable[i].y, myTable[i].diam) end end su dimensión es el resultado de multiplicar el ancho por el alto. Al igual que los array lineales, empieza a contar por cero y el primer elemento tiene el ID [0,0] Teniendo en mente este concepto, vamos a ver cómo GAmuza no usa exactamente arrays, sino lo que en el lenguaje Lua se denominan tablas (librería table de Lua), pero puede hacer las mismas operaciones con mayor flexibilidad porque las tablas permiten utilizar La variable i de la estructura for va adoptando consecutivamente el ID de cada circulo de la tabla. datos de diferente tipo y simplifican mucho el proceso de programación. Además, las tablas en Lua no se entienden como lineales o de dos dimensiones, sino que una tabla lineal puede albergar múltiples dimensiones de datos. La sintaxis para declarar una tabla es: Después de crearla con myTable[i] = {}, no necesita establecer nuevos arrays para cada uno de los parámetros de la función of.circle, simplemente en el mismo for va asignando esos valores utilizando el operador “.” junto al código que representa cada dato de la tabla: myTable[i]. En la función draw(), además de las funciones internas para el color y no relleno, otra estructura nombreTabla {} for dibuja todos los círculos recogiendo los datos de la tabla. No hay función update() porque en Para crearla pueden enumerarse los elementos que la constituyen entre {}, definir cual es el número de elementos por medio de una variable, o simplemente crear la tabla y todos los objetos utilizando el ejemplo las formas están fijas, no hay comportamiento. Sí se incluye en el siguiente ejemplo en el que se incrementan las propiedades de cada uno de los objetos de la tabla que vuelven a ser círculos. una expresión for que recorrerá la tabla y asignará valores a todos los datos. Pasamos a describirlo comentado el código de algunos ejemplos. /* GAmuza 0.4.1 examples --------------------Basics/tabla_circulosFijos.ga Cómo declarar una tabla y asignar valores a sus elementos created by n3m3da | www.d3cod3. org */ myTable = {} function setup() of.setCircleResolution(60) for i = 0, 100 do -- establece nº objetos del array myTable[i] = {} myTable[i].x = of.randomuf()*OUTPUT_W myTable[i].y = of.randomuf()*OUTPUT_H myTable[i].diam = of.random(0,OUTPUT_W/2) end end 128 ----- crea la tabla asigna valores a posición x asigna valores a posición y valores a diámetros /* GAmuza 0.4.1 examples --------------------Basics/tabla_circulosRebotan.ga asignar comportamiento a los objetos de una tabla */ 129 circulos ={} maxCirculos = 20 -- se declara la tabla -- número máximo de objetos function setup() of.enableSmoothing() of.setCircleResolution(50) for i = 0, maxCirculos-1 do -- recorre todos los objetos circulos[i] = {} -- crea tabla y asigna propiedades circulos[i].posX = of.random(30, OUTPUT_W) -- coordenadas X al azar circulos[i].posY = of.random(OUTPUT_H/2) -- coordenadas Y al azar circulos[i].diam = of.random(15, 100) -- diámetro círculos circulos[i].velX = 0.000000 -- velocidad X circulos[i].velY = 0.000000 -- velocidad Y circulos[i].gY = 0.00184562 -- gravedad Y circulos[i].gX = of.random(-0.018326, 0.044326) -- gravedad X circulos[i].color= of.random(80, 255) -- gris azar cada objeto circulos[i].transp = 220 --transparencia end end function update() for i=0, maxCirculos-1 do circulos[i].posX = circulos[i].posX + circulos[i].velX -- posición + velocidad circulos[i].posY = circulos[i].posY + circulos[i].velY circulos[i].velX = circulos[i].velX + circulos[i].gX -- velocidad + gravedad circulos[i].velY = circulos[i].velY + (circulos[i].gY*circulos[i].diam) if circulos[i].posX <= 0 or circulos[i].posX >= OUTPUT_W-10 then circulos[i].velX = circulos[i].velX * -0.972773 end -- invierte dirección al tocar los bordes if circulos[i].posY >= OUTPUT_H or circulos[i].posY <= 0 then circulos[i].velY = circulos[i].velY * -0.962722 end end end function draw() ga.background(0.0, 1.0) for i= 0, maxCirculos-1 do -- dibuja todos los círculos of.setColor(circulos[i].color, circulos[i].transp - circulos[i].diam) dibujaCirculos(circulos[i].posX, circulos[i].posY, circulos[i].diam) end -- dibuja círculos usando la función de abajo end En el bloque del setup() se asignan las propiedades del objeto utilizando el operador “.” junto al nombre de la tabla y el identificador del array de objetos que contiene [i] nombreTabla[i].Propiedad El for recorre el array, reserva la memoria que el sistema necesita para estos objetos y les asigna las propiedades definidas, que en este caso son: posición X y posición Y (para localizar el centro de cada circulo), radio (tamaño), velocidad X y velocidad Y (para el movimiento de los círculos) gravedad X y gravedad Y (para representar los rebotes y un movimiento menos lineal), color y transparencia. Los valores de i van desde la posición 0 a la que define la variable maxCirculos-1, porque empieza a cotar desde 0, y el objeto 20 está en el contenedor con ID 19. Los valores de las propiedades que se asignan pueden cambiar, pues un objeto puede tener distintos valores en momentos diferentes, pero siempre es el mismo objeto. Así, a las propiedades de velocidad, gravedad y transparencia se le han asignado inicialmente unos valores y después cambian. Los comportamientos se establecen en la función update(), donde se actualizan los datos de las variables que definen las propiedades. Con un nuevo for , a las coordenadas de posición de cada objeto se le suman las de velocidad, y a las de velocidad, las de gravedad en la coordenada de las X y las de gravedad por el diámetro del círculo en la coordenada Y, este incremento produce un movimiento más físico de peso del círculo. Después se ajustan los rebotes a los límites de la ventana mediante bloques condicionales if, invirtiendo la dirección del movimiento al multiplicar el valor de su velocidad por un número negativo con muchos decimales que no llega a 1. La función dibujaCirculos() define la forma de los objetos, en este caso los parámetros son los mismos que los de la función interna del circulo of.circle(), pero como vimos en el ejemplo anterior, no siempre es así. El siguiente ejemplo, más complejo, está basado en la noción de Random Walker, utiliza varios arrays y la función of.noise() vista en la página 108. function dibujaCirculos (posX, posY, diam) of.circle(posX, posY, diam) -- función que define la forma de los objetos end 130 131 function draw() /* GAmuza 0.4.1 examples --------------------Basics/tabla_noiseWalk.ga Basado en Daniel Shiffman - Nature Of Code created by n3m3da | www.d3cod3.org */ walkers = {} noiseTimeX = {} noiseTimeY = {} stepX = {} stepY = {} numW = 300 function setup() for i = 0,numW-1 do walkers[i] = {} walkers[i].x = OUTPUT_W/2 walkers[i].y = OUTPUT_H/2 noiseTimeX[i] = of.random(0,10000) noiseTimeY[i] = of.random(7000,14000) stepX[i] = of.random(0.001,0.003) stepY[i] = of.random(0.001,0.003) end end function update() for i=0,numW-1 do mapX = of.map(of.noise(noiseTimeX[i]),0.15,0.85,0,OUTPUT_W,true) mapY = of.map(of.noise(noiseTimeY[i]),0.15,0.85,0,OUTPUT_H,true) walkers[i].x = mapX walkers[i].y = mapY 132 noiseTimeX[i] = noiseTimeX[i] + stepX[i] noiseTimeY[i] = noiseTimeY[i] + stepY[i] end end ga.background(0.0,0.01) of.noFill() for i=0,numW-1 do if math.fmod(i,7) == 0 then -- Devuelve el resto de la división de i por 7 -- y redondea el cociente a cero. of.setColor(255) else of.setColor(0) end of.rect(walkers[i].x,walkers[i].y,1,1) end end Algunas funciones internas vinculadas a las tablas63 table.getn(myTable) Para saber el número de elementos de una tabla table.foreach(myTable, MyFunction) Recorre cada índice de la tabla y le asigna la función (el segundo parámetro dentro del corchete). table.insert(myTable, [position], value) -- ejemplo (myTable, 5, true) Esta función añade un valor al final de la tabla. Si se señala una posición exacta, para colocar el valor los índices siguientes se desplazan en consecuencia. table.remove(myTable, [pos]) -- ejemplo (myTable, 3) Elimina un elemento de una tabla y mueve hacia arriba los índices restantes si es necesario. Si no se asigna posición borra el último elemento. 63 Más información en “Lua for Beginers” [texto on-line] http://lua.gts-stolberg.de/en/table.php [29.07.2012] 133 3.9.2. Clases y objetos En una clase, el concepto más destacado es el de objeto, representa cualquier cosa, real o abstracta, (componente conceptual del problema planteado). En la programación, un objeto se define en dos partes: 1) Constructor: una serie de datos que constituyen el estado particular (atributos o propiedades) del objeto y que se formalizan mediante un conjunto de variables. 2) Update: comportamiento propio de los objetos que se formaliza mediante métodos (funciones de objetos) relacionados con las variables. Como se ha visto en los ejemplos anteriores, el objeto círculo tiene una serie propiedades declaradas con variables según lo que requiera la situación planteada: coordenadas del centro, radio o diámetro, color, transparencia, grosor del trazo..., de ellas, las que sean necesarias para definir el objeto serán los parámetros del constructor. El comportamiento quedará regulado por los métodos que programemos para que esos objetos circulo situen sus coordenadas fijas o en movimiento, sigan una dirección de movimiento u otra, reboten con los límites de la ventana, etc., Definición de Clase Estableciendo una relación algo simplista podemos entender las clases aludiendo a la Teoría de las ideas (o Teoría de las formas) de Platón. Las clases se corresponderían al mundo inteligible donde están las estructuras o modelos (las ideas) en las cuales se basan las cosas físicas concretas (los objetos) que podemos percibir en el mundo sensible. Por ejemplo, la clase círculos puede contener todos los objetos circulares, con sus distintos tamaños, colores, etc., y sus distintos comportamientos. El objeto círculo es una instancia de la clase círculos. Cada objeto círculo tiene un estado particular independiente, pero todos tienen en común el hecho de tener una variable color, radio, etc. E igualmente deben tener en común la posibilidad de adoptar determinados comportamientos, por lo que la clase debe declarar o implementar los métodos que regulan el comportamiento de los objetos que contiene. En resumen, una clase es como una plantilla que define las variables y los métodos que son comunes para todos los objetos que agrupa, en esta plantilla debe haber siempre un bloque dedicado al constructor que lleva el mismo nombre que la clase y define los parámetros necesarios para crear o instanciar un objeto de la clase. Herencia Es fácil comprender que la clase círculos pertenece a una clase mayor que alberga las figuras geométricas planas. Si en nuestro trabajo planteamos ambos niveles, un círculo concreto debe 134 responder tanto a las propiedades de la clase círculos como a la de la clase figuras_planas, por lo que compartirá esos atributos y comportamientos con objetos de otras posibles clases, como la clase triángulos o la clase cuadrados. Esta herencia no limita las posibilidades de particularización de la clase círculos, más bien el orden de pensamiento es inverso al establecido aquí. La herencia permite definir nuevas clases partiendo de las existentes, de modo que heredan todos los comportamientos programados en la primera y pueden añadir variables y comportamientos propios que la diferencian como clase derivada o subclase. Y así se puede establecer un árbol de herencias tan ramificado como se necesite. Mensajes En la imagen anterior, además de ver un diagrama de herencia también muestra en una de las flechas que va de la clase triángulos a la clase rectángulos como se establece un mensaje. Es a través de los mensajes como se produce la interacción entre los objetos, permitiendo así programar comportamientos más complejos. Por ejemplo, cuando un objeto triángulo cumple una determinada condición, puede enviar un mensaje a un objeto rectángulo para que realice alguno de los comportamientos que su clase (la del rectángulo) tiene definidos, en el mensaje se pueden incorporar determinados parámetros para ajustar el comportamiento. Un mismo mensaje puede generar comportamientos diferentes al ser recibido por objetos diferentes. A esta característica se le denomina polimorfismo. Es conveniente hacer diagramas de las clases con los atributos y comportamientos de los objetos antes de empezar a escribir el código, para respondernos a las preguntas: – ¿Cuántas clases necesitamos definir y que herencia se establece entre ellas? – ¿Dónde definimos cada propiedad? – ¿Dónde definimos cada método? – ¿Dónde implementamos cada método? 135 3.9.3. Clases en GAmuza La herencia se implementada utilizando el concepto de metatablas, que actúan como superclases, y la función setmetatable(). El código __index almacena la metatabla de la tabla. En el siguiente código la tabla Rectangulo deriva de la tabla Formas65: Lua does not have a class system, but its powerful metaprogramming facilities makes defining classic objects straightforward.64 Rectangulo = {} setmetatable(Rectangulo, {__index = Formas}) En el lenguaje Lua, la programación orientada a objetos (POO) puede realizarse a través de tablas. Una tabla, además de funcionar como un array, también puede hacerlo como un objeto, porque las tablas tienen un estado y una identidad (self) que es independiente de sus valores, como hemos Algunos ejemplos comentados de clases simples. mencionado antes, dos objetos con el mismo valor son objetos diferentes, un objeto puede tener distintos valores en momentos diferentes, pero siempre es el mismo objeto. Al igual que los objetos, las tablas tienen un ciclo vital que es independiente de aquello que lo creó, o donde se crearon. Hemos visto en el apartado arrays como en una tabla se asignan propiedades a cada elemento mediante el operador “.”, ahora veremos como se asocian métodos (funciones de objetos) a los objetos de una clase mediante el operador “:”. La sintaxis para las clases en GAmuza es class ‘nombreClase’ Se hace el constructor con una función que lleva el nombre de la clase y se inicializa con function nombreClase:__init (parametro1, parámetro2,...) Después del nombre de la clase van dos puntos “:” y dos guiones bajos “__” seguidos de init y los parámetros que definen la construcción del objeto, entre paréntesis y separados por “,”. La clase tiene su propio método update() que se escribe: function nombreClase:update() donde se pone el código para actualizar los valores de las variables y programar el comportamiento de los objetos. Y su método draw() que contiene el código de las formas que va a dibujar. function nombreClase:draw() Después, se crean los objetos de la clase dándole los valores concretos a los parámetros definidos en el constructor. ob1 = nombreClase(valorParametro1, valorParámetro2,...) 64 Lua-users wiki: Simple Lua Classes [texto on-line] < http://lua-users.org/wiki/SimpleLuaClasses > [25.07.2012] 136 /* GAmuza 0.4.1 examples --------------------Basics/classExample.ga Este ejemplo muestra el código básico para construir una clase. created by mj */ class ‘foco’ ---constructor function foco:__init(x, d, s, g) --define parámetros y propiedades del objeto self.posX = x -- posición Y self.diam = d -- diámetro self.speed = s -- velocidad self.grav = g -- gravedad end 65 Marcelo da Silva Gomes, “The Shape Example in Lua” [texto on-line] <http://onestepback.org/articles/poly/gp-lua.html> [25.07.2012] 137 function foco:update() -- actualiza parámetros para el comportamiento self.posX = self.posX + self.speed self.speed = self.speed + (self.grav*self.diam) -- condicionales: cuando llegue a los topes cambia de dirección if self.posX > OUTPUT_W - self.diam/2 then self.speed = self.speed * -0.999722 end if self.posY < 0 + self.diam/2 then self.speed = self.speed * -0.999722 end Después de declarar la clase: class ‘foco’; se define el constructor, es decir, ya sabemos los parámetros que definen las propiedades concretas de los objetos que queremos crear. El código function nombreClase:__init (parametro1, parámetro2,...) en el ejemplo corresponde a: function foco:__init(x, d, s, g), y en esta función (constructor), utilizando el operador “.”, se define el estado de las propiedades para la identidad (self) de cada objeto. Las clases, además del self, tienen sus propios métodos update() y draw() que se asocian a la clase mediante el operador “:” function foco:update() function foco:draw() En la primera define las funciones del comportamiento: suma el valor de la posición X a la velocidad, suma el valor de la velocidad al de la gravedad; establece las condiciones para cambiar el sentido de la end velocidad cuando la coordenada X llega a los límites de la ventana. La segunda, contiene las funciones function foco:draw() -- función para dibujar la forma del objeto of.circle(self.posX, OUTPUT_H/2, self.diam) end internas que dibujan la forma. Después de definir la clase se crean el objeto u objetos concretos, dando valores específicos a los parámetros determinados en el constructor. En este caso, los parámetros (y, d, s, g) para el objeto -------- crear 2 objetos de la clase foco foco1 = circulo(25, 50, 3.5, 0.00184562) foco2 = foco(125, 30, 1.5, 0.00184562) ----------------------------------------function setup() of. enableSmoothing() of.setCircleResolution(50) end foco1 son (25, 50, 3.5, 0.00184562), es decir, el objeto aparece en la coordenada y = 25, tiene un diámetro de 50 píxeles, se mueve a una velocidad de 3.5 y con una gravedad de 0.00184562. En las funciones habituales de un programa en GAmuza, el código llama a los métodos de la clase. En function update() foco1:update() En function draw() foco1:draw() function update() foco1:update() foco2:update() Es importante observar las diferencias entre clases y array, cuando una tabla funciona como array las end propiedades se definen en el setup() por el ID del array [i] utilizando un for para recorrer todos function draw() función update() y dibuja las formas en la función draw() del programa. los objetos. Además el array no tiene métodos propios, por lo que actualiza su comportamiento en la ga.background(0.0, 1.0) of.setColor(255) foco1:draw() foco2:draw() Según el tipo de planteamiento a resolver es mejor estructurarlo por arrays o por clases. El siguiente ejemplo utiliza clases y arrays. end 138 139 if blackout then c:set(0,0,0,24) else c:set(255,255,255,24) end end -- hacer filamentos blancos -- hacer filamentos negros function Particle:update() self.xx = self.x self.yy = self.y -- posición x’= a la de x -- posición y’= a la de y self.x = self.x + self.vx self.y = self.y + self.vy self.vx = self.vx + (of.randomf()/2) self.vy = self.vy + (of.randomf()/2) /* GAmuza 0.4.1 examples --------------------Artists/binaryRing.ga This example is from Binary Ring66 piece by Jared Tarbel, 2004. created by n3m3da | www.d3cod3.org */ c = of.color() blackout = false kaons = {} numKaons = 1000 class ‘Particle’ -- hacer filamentos blancos o negros -- declara la tabla -- declara la Clase function Particle:__init(Dx,Dy,r) self.Dx = Dx self.Dy = Dy self.r = r self.ox = OUTPUT_W/2 self.oy = OUTPUT_H/2 self.x = math.ceil(self.ox-self.Dx) self.y = math.ceil(self.oy-self.Dy) self.xx = 0 self.yy = 0 self.vx = 2*math.cos(self.r) self.vy = 2*math.sin(self.r) self.age = of.random(0,200) ---------- posición x círculo posición y círculo posición x posición y posición x’ posición y’ velocidad x velocidad y edad 66 Este ejemplo retoma el trabajo del artista Jared Tarbell y lo realiza en GAmuza. Código original en: http://www.complexification.net/gallery/machines/binaryRing/appletm/BinaryRing_m.pde [29.07.2012] 140 self.age = self.age + 1 if self.age > 200 then local t = of.random(TWO_PI) self.x = 3*math.sin(t) self.y = 3*math.cos(t) self.xx = 0 self.yy = 0 self.vx = 0 self.vy = 0 self.age = 0 -- posición x = x + velocidad x -- posición y = y + velocidad y -- velx = velx + random(-1,1)/2 -- vely = vely + random(-1,1)/2 -- cada vuelta tienen más edad -- si llegan al valor 200 if blackout then c:set(0,0,0,24) else c:set(255,255,255,24) end end end function Particle:draw() of.noFill() of.setColor(c) -- define los parámetros de las lineas of.line(self.ox+self.xx,self.oy+self.yy,self.ox+self.x,self.oy+self.y) of.line(self.ox-self.xx,self.oy+self.yy,self.ox-self.x,self.oy+self.y) end function setup() ga.background(0.0, 1.0) for i=0,numKaons-1 do -- define los elementos de la tabla local emitx = math.ceil(3*math.sin(TWO_PI*i/numKaons)+OUTPUT_W/2) local emity = math.ceil(3*math.cos(TWO_PI*i/numKaons)+OUTPUT_H/2) local r = PI*i/numKaons kaons[i] = Particle(emitx,emity,r) -- los vincula con la clase end end function update() 141 for i=0,numKaons-1 do kaons[i]:update() -- a cada elemento de la tabla da el update de la clase end local r = of.randomuf() if r > 0.9991 then blackout = not blackout end end 4. Interactividad 4.1. Definiciones de interactividad function draw() --ga.background(0.0,1.0) for i=0,numKaons-1 do kaons[i]:draw() -- a cada elemento de la tabla da el draw de la clase …I do think I can interact with a cat. That’s a feeling I have. But not with an ant, for example… Jim Campbell67 end end function mouseReleased() blackout = not blackout end Todos las acciones perceptivas implican un grado de interactividad con los estímulos que las generan y su entorno. A través de los sentidos recogemos datos (inputs), nuestra mente los codifica y/o reflexiona sobre ellos (procesa) y como resultado pueden modificar nuestro conocimiento, comportamiento, estado de salud, ánimo, etc., (output). Pero desde el contexto de los medios digitales se ha generado un nuevo enfoque del término interactividad que tiene que ver con ese mismo proceso, pero restringe el ámbito a la relación humano-máquina. En nuestro caso vamos a restringir un poco más para analizarlo sólo en la relación que se produce entre espectadores y piezas en el arte interactivo, si bien para analizarlo en ese contexto recurriremos a veces a su consideración más general. Con la siguiente selección de definiciones y argumentos queremos aportar una panorámica amplia de las distintas posiciones que artistas y teóricos del arte han adoptado en este tema. Algunas son muy extensas, sí, pero dibujan bien el cambio que se ha producido desde los pioneros hasta la actualidad. Myron W. Krueger: […] observando a los participantes que interactuaban con las piezas, he desarrollado una fuerte intuición sobre lo que la gente puede llegar a entender de lo que está haciendo y lo que quieren que suceda. Mi idea de interacción surgió de esta observación de los participantes, más que de ideas preconcebidas, evitando la creación de un arte que suena muy impresionante cuando se describe por escrito, pero que no tiene éxito con su público. Esto fue poco después de las instalaciones originales, tras dieciséis años de desarrollar las capacidades de la visión por ordenador necesarias para que el trabajo empezara a ser autónomo. Todo el trabajo posterior se ha basado en una estética de los interactivos aprendida en las primeras piezas: El arte debe ser divertido y accesible. 67 Richard Whittaker (1999) Jim Campbell: Frames of Reference [texto on-line] <http://www.conversations.org/story.php?sid=30> [28.08.2012] 142 143 La interacción física es una nueva dimensión que debe ser explorada. Aunque nuestra cultura sólo permite a los adultos modos limitados de movimiento físico, el arte interactivo puede inducir a moverse en nuevas formas. Stephen Wilson La interactividad es a menudo considerada el rasgo distintivo de los medios basados en el ordenador. El público está invitado a actuar para influir en el curso de los acontecimientos, o para navegar por el hiperespacio de datos. Al principio, esta relación entre público y obra se consideró una aportación bastante radical, que transformaba a artista y público en co-creadores lo que incrementaba las probabilidades de un compromiso intelectual, espiritual y estético. A medida que el campo ha madurado, tanto artistas como teóricos han tratado de deconstruir la interactividad.69 Las pantallas deben dominar el campo de visión de los participantes para que se sumerjan en la experiencia interactiva. La imagen de los participantes es un ingrediente útil en la pantalla visual. La gente considera su imagen como una extensión de su identidad. ¿Qué pasa con él, que les pasa. Qué tocan, ellos sienten. Las respuestas del ordenador deben ser obvias. Siempre debe haber un nivel superficial, que la gente pueda entender en pocos segundos. En un mundo en el que las experiencias interactivas compiten con muchas otras Alternativas, una pieza que no es aprehendida casi de inmediato se abandona rápidamente. Todos los estímulos generados por el ordenador deben ser una respuesta a las acciones de los participantes. Cuando el equipo inicia estímulos gratuitos, la capacidad de los participantes para comprender lo que está pasando se ve amenazada. 1. No es necesario el realismo gráfico para una interacción convincente. De hecho, los entornos gráficos realistas suelen aportar confusión y son menos claros que las posibilidades interactivas que el mundo virtual ofrece. 2. La interacción entre el participante y el mundo virtual debe ser fluida y sin problemas. Esto significa que las respuestas debe ser instantáneas, tal como lo serían en el mundo real. Así como un taxi que corre por una pista de despegue, pero no se mueven lo suficientemente rápido como para despegar no es un avión, una experiencia interactiva en la que el participante es consciente de un desfase entre su acción y la respuesta de la pieza no es interactiva. 3. Dando a la audiencia muchas interacciones diferentes para elegir los mantienes interesados por más tiempo. 4. Si se ofrece un lienzo, pincel y pinturas de óleo en un lugar público, muy poca gente tratará de crear arte, pero cuando se enfrentan a la posibilidad de utilizar un medio en el que las reglas son desconocidas y con el que las posibilidades de éxito estético son Altas, la mayoría de la gente moverán sus cuerpos para crear un resultado que les agrade. De hecho, la exploración estética ofrece una nueva razón para mover el cuerpo. 5. Idealmente, una pieza debería hacer algo que tenga sentido en todas las condiciones de exposición: lleno frente vacío, ruidosa frente silenciosa, individuos aislados frente pequeños grupos. 6. Mientras que los videojuegos se basan en los resultados para motivar la participación en experiencias muy estructuradas, la participación física permite desarrollar otros estilos de interacción. 7. Cargar con dispositivos como head-mounted displays distancia al participante de la experiencia virtual y aún no son suficientemente buenos para utilizarlos.68 68 Myron W. Krueguer, ‘Towards Interactive Aesthetics’, en Ars Electronica Katalogartikel <http://90.146.8.18/en/archives/festival_ archive/festival_catalogs/festival_artikel.asp?iProjectID=12978> [20.02.2012]. 144 Joshua Nobel Hacer arte interactivo es muy diferente de hacer arte no interactivo porque el verdadero objeto de arte interactivo es la situación. En pintura, el objeto es la propia pintura, en escultura, es el objeto y el espacio que lo rodea, en una pieza de vídeo, el objeto es la proyección de vídeo. En una obra de arte interactivo, el objeto de arte es realmente la interacción entre el observador y el sistema que el artista ha creado. Ese sistema puede ser muy complejo tecnológicamente, puede tener un único elemento técnicamente simple, o puede tener ninguno. 70 […] La interactividad es un concepto que tiende a utilizarse para evadir las descripciones de los mecanismos tecnológicos de los medios, y como resultado, con demasiada frecuencia elude una atención analítica y crítica sostenida71. Erkki Huhtamo, […] la tecnología interactiva soporta una gran promesa. Bien podría subvertir las prácticas de media art predominantes y sustituirlas con formas más versátiles, amigables y “democráticas”. Sin embargo, dirigirse solo al desarrollo de hardware, diseño de interfaz y curvas de ventas no será suficiente para lograr este objetivo. La tecnología digital han de ser valorada en un contexto más amplio que abarca no sólo el espectro completo de las prácticas contemporáneas sociales e ideológicas, sino la historia también. Este artículo ha tratado de demostrar -aunque a través de muestreo muy selectivo- que las obras de arte interactivas pueden tener un papel fundamental en este proceso, manteniendo un metadiálogo continuo sobre la interactividad. Esto no es una tarea fácil. Como Andy Darley ha dicho de manera sucinta: «Las posibilidades de formas constructivas igualitarias, más democráticas, que ofrezcan nuevas formas del interacción, conocimiento y comprensión puede ser mejorada por la capacidad novedosa de las nuevas tecnologías. Y más que nunca, tienen que esforzarse en ello».72 Estas referencias señalan algunas características del arte interactivo, y su transformación desde la interactividad directa y casi mimética de los pioneros, muy vinculada a la relación humano-máquina 69 Stephen Wilson, (2002) Information Arts: Intersections of Art, Science, and Technology (Cambridge (MA): MIT Press), pág. 653. 70 Joshua Noble, Programming Interactivity. A Designer’s Guide to Processing, Arduino, and openFrameworks (Sebastopol (CA): O’Reilly, 2009), pág. 14. 71 Ibidem, pág. 87. 72 Erkki Huhtamo, (1992) ‘Seeking Deeper Contact. Interactive Art as Metacommentary’, [texto on-line] <http://www.kenfeingold. com/seekingdeeper.html> [17.06.2012]. 145 (human computer interaction73), la interactividad indirecta, hasta el metalenguaje interactivo que señala Siempre he pensado que el sentido que se le da al término interactividad en el trabajo con Huhtamo. ordenadores estaba equivocado. Me refiero a que el sentido histórico de la “interactividad” siempre En la interactividad directa, la experiencia del espectador habitualmente se debate ante el desafío de encontrar los mecanismos de interacción (su funcionamiento técnico) y lo que la obra quería transmitir, llegando a tener una experiencia satisfactoria semejante a la que se alcanza cuando consigues superar nivel en un videojuego, pero a veces distante al tipo de fruición y reflexión propios del arte. A finales de los 90’s algunas propuestas empezaron a cuestionar el sentido de la interactividad en el arte digital, mostrando dos disyuntivas: por una parte, la interactividad directa entre el espectador y la pieza, en la que el rol del espectador se aproximaba al performer; por otra parte, el contenido de la obra se configura a través de la interactividad del los mecanísmos técnicos de la pieza con datos obtenidos del entorno físico o del flujo da datos de la red. Con ello se desplazaba la noción de interactividad desde la relación espectador/obra [humano-máquina] hacia entorno/obra pudiendo el ha estado vinculado a un suceso de comunicación mutua, recíproco. Tal como lo veo, lo que realmente se establece con los ordenadores es control. Se les controla para hacer algo [o se controla al espectador para hacer algo]. Las piezas dedicadas a Heisenberg sirven para mostrar estos problemas de una forma directa: cuanto más se quiere ver algo, más se intenta controlar la situación, pero entonces se ve menos y cuando menos lo controlas, más posibilidades tienes de ver. Por ejemplo, en la pieza “Sin título para Heisenberg” cuando se camnia haacia la cama, tienes la imagen cada vez más cerca, pero nunca se verán los poros de la piel, lo que se ve son píxeles. Cuando estás más lejos se puede ver a los dos amantes, pero por el hecho de aproximarte no puedes ver más. La pieza del Buda responde de forma similar. Desde la distancia se puede ver la estatua de Buda, pero a medida que te acercas no ves más que su sombra. ¿Qué estás viendo? Ambas piezas haacen referencia al principio de Heisenberg. ¿Lo conoces?75 espectador participar en los datos que proporciona el entorno, o no. En el año 2002, la obra Listening Post74, de Ben Rubin y Mark Hansen, subrayó ese desplazamiento del concepto de interactividad, hacia otro tipo de relación entre: inputs -- procesamiento - output = obra en continuo proceso La salida, la configuración de la obra, subraya aun más el valor del proceso. La pieza combina de forma sugerente la relación de entrada dinámica de datos en tiempo real con el debate de espacio privado/ publico en la red. Esa entrada dinámica de datos está constituida por la selección de fragmentos de texto en tiempo real de miles de salas de chat de Internet no restringidas, tablones de anuncios y otros foros públicos en la red. El procesamiento de esos datos tiene como resultado que los textos seleccionados son cantados por un sintetizador de voz, y al mismo tiempo se muestran a través de más de doscientas pantallas electrónicas pequeñas que construyen una membrana reticular curva. La participación de los espectadores es secundaria y su tarea es interpretar la instalación, su función y concepto, a través de una experiencia con el ambiente casi ceremonioso que genera la obra y su alusión ritualizada sobre el flujo de información de las redes sociales. Por último retomamos otro fragmento de la entrevista a Jim Campbell que inicia este apartado: 73 Interacción persona-ordenador: Es la disciplina que estudia el intercambio de información mediante software entre las personas y las computadoras. Ésta se encarga del diseño, evaluación e implementación de los aparatos tecnológicos interactivos, estudiando el mayor número de casos que les pueda llegar a afectar. El objetivo es que el intercambio sea más eficiente: minimizar errores, incrementar la satisfacción, disminuir la frustración y, en definitiva, hacer más productivas las tareas que rodean a las personas y los computadores. [enciclopedia on-line] http://es.wikipedia.org/wiki/Interaccion_persona-computador [05.07.2012] 74 Listening Post - Ear Studio < http://earstudio.com/2010/09/29/listening-post/> [04.09.2012] 146 75 Richard Whittaker (1999) Jim Campbell: Frames of Reference [texto on-line] <http://www.conversations.org/story.php?sid=30> [28.08.2012] 147 4.2. Código para interactividad. Eventos de ratón El objetivo es plantear sistemas interactivos más complejos y conceptuales, menos directos, pero para llegar hasta ellos hay que recorrer los primeros pasos, conocer la base interactiva de las interfaces gráficas. Los dispositivos más comunes para interactuar con elementos gráficos en la pantalla son el ratón y el teclado. Tras un breve repaso se plantearan otras opciones más próximas al “metadiálogo La relación inversa entre la dimensión de la ventana de salida respecto a la posición del ratón hace que el movimiento responda a direcciones con sentido contrario. También puede aplicarse el valor de las coordenadas X,Y del ratón a las dimensiones de las formas que cambian de tamaño con el movimiento del ratón. function setup() sobre la interactividad” que señala Huhtamo, pero hay que empezar a andar. Situados ante la pnatalla, para el diseño de interactividad con el ratón, los datos que el sistema necesita function update() conocer son las coordenadas X e Y de su posición, si se ha movido, si algún botón está presionado, ha sido soltado o es arrastrado. Las funciones que nos dan esos datos son: ga.mouseX(), ga.mouseY() -- coordenadas x e y de la posición del ratón Posición del ratón Cuando el ratón está en el lado izquierdo de la pantalla su coordenada es 0, y se incrementa conforme se desplaza hacia la derecha. Cuando el ratón está en el lado superior de la pantalla, el valor de la end end function draw() se pone en marcha la posición del ratón es (0,0) y el código escrito en el bloque setup()solo lo lee una vez. Usualmente se establecen relaciones interactivas entre la posición del ratón y la ubicación de formas, como en: -- color amarillo of.setLineWidth(20) -- ancho línea 20px of.line(ga.mouseX(), ga.mouseY(), 200, 200) -- la línea tiene una coordenada fija -- color verde of.noFill() -- no rellenar of.circle(OUTPUT_W/2, OUTPUT_H/2, ga.mouseX()/2) end relación como de bombeo flexible, al estirar la línea el círculo crece. ga.background(0.0, 0.5) of.setColor(255) of.circle(mouseX(), mouseY(), 50) -- el centro de círculo está GAmuza articula los eventos de ratón a través de bloques de función que permiten programar acciones -- en la posición del ratón que serán efectuadas cuando esos eventos sucedan. function mouseDragged() -- Mover el ratón con el botón apretado (arrastrar) function mouseMoved() -- Movimiento del ratón end Pueden establecerse relaciones menos evidentes si se combinan operadores matemáticos que aumenten o disminuyan determinados valores para que las relaciones visuales que se producen entre las formas y la posición del ratón sean indirectas, por ejemplo: function draw() ga.background(0.0, 0.5) of.setColor(255) of.circle(OUTPUT_W-mouseX(), mouseY()- OUTPUT_H, 50); end 148 -- y otra en la posición del ratón of.setColor(0,255,0) Los cambios entre la línea y el círculo están vinculados a la posición del ratón estableciendo una function draw() end ga.background(0.0,1.0) of.setColor(255,255,0) coordenada y es 0 y se incrementa conforme va bajando. Estos datos NO los da el sistema si las funciones ga.mouseX()y ga.mouseY() se sitúan en el bloque setup() porque cuando el programa of.setCircleResolution(50) end function mousePressed() end -- Botón del ratón apretado function mouseReleased() -- Botón del ratón soltado end El siguiente ejemplo responde al movimiento del ratón - mouseMoved()- que equivale a tamaño de las formas vinculado a su posición y clicar el botón, mousePressed() 149 /* GAmuza 0.4.1 examples --------------------Basics/flutua_dispersa.ga Interactividad mouseDragged y mouseMoved */ x = OUTPUT_W/2 En este caso se utiliza la función mouseMoved() para modificar el valor de las variables fluctua y dispersa según se mueva el ratón por la ventana de salida. La variable fluctua controla el límite de repeticiones de una estructura for (el número de circulos pequeños que construyen el anillo) y la variable dispersa controla el radio del anillo de círculos generado por el for. Con la función mousePressed() se desplaza el centro del anillo a la zona de la ventana de salida donde clique el ratón. Tras clicar en cualquier punto de la ventana de salida, si se mueve el ratón diagonalmente se verán los cambios. La transparencia del fondo permite ver el rastro de los cambios. y = OUTPUT_H/2 r = 2 angle = 0.0 En el siguiente ejemplo se establecen 5 opciones de composión formal diferentes cuyo cambio está fluctua = 20 vinculado a la acción de pulsar el botón del ratón dispersa = 5 /* GAmuza 0.4.1 examples --------------------ReasMcWilliamsLUST_FORM+CODE/EmbeddedIteration.ga function setup() of.enableSmoothing() end function draw() ga.background(0.0, 0.2) of.setColor(255) for i = 0, fluctua do angle = of.random(TWO_PI) dispX = x + math.cos(angle)*dispersa dispY = y + math.sin(angle)*dispersa of.circle(dispX, dispY, r*2) end end function mousePressed() x = ga.mouseX() y = ga.mouseY() end Repeat: Embedded Iteration from Form+Code in Design, Art, and Architecture by Casey Reas, Chandler McWilliams, and LUST Princeton Architectural Press, 2010 http://formandcode.com created by n3m3da | www.d3cod3.org */ option = 1 -- variable global function setup() of.enableSmoothing() end function draw() ga.background(0.0,1.0) of.setColor(245,151,176) if option==1 then function mouseMoved() dispersa = ga.mouseX()*0.5 fluctua = ga.mouseY()*0.5 -- stitches for x=50, OUTPUT_W-50, 20 do for y=50, OUTPUT_H-50, 20 do of.line(x-5,y-5,x+5,y+5) of.line(x+5,y-5,x-5,y+5) end end end 150 151 elseif option == 2 then -- perspectiva for y=50, OUTPUT_H-50, 20 do of.line(x,y,OUTPUT_W/2,OUTPUT_H/2) end elseif option == 3 then -- círculos superpuestos of.noFill() for x = 50, OUTPUT_W-50, 20 do for y = 50, OUTPUT_H-50, 20 do end of.ellipse(x,y,40,40) end elseif option==4 then -- arcos girando for x = 50, OUTPUT_W-50, 20 do for y = 50, OUTPUT_H-50, 20 do of.line(x-5,y-5,x+5,y+5) end elseif option == 5 then -- grupos de cinco for x = 50, OUTPUT_W-50, 20 do for y = 50, OUTPUT_H-50, 20 do for i = 1, 16, 4 do of.line(x+i,y,x+i,y+12) end of.line(x,y,x+12,y+12) end señal de entrada al sistema que tiene como respuesta (salida) la impresión de caracteres en el monitor. código. Vamos a ver un ejemplo en el que el teclado genera sonidos en lugar de palabras. /* GAmuza 0.4.1 examples --------------------Audio/keyboardSynth.ga Este ejemplo transforma el teclado en un sintetizador de audio random (muy básico) Comprobar que está activado un canal de salida de audio en el Setting Manager created by n3m3da | www.d3cod3.org */ function setup() ga.wave(GA_TRI,120) end end El teclado es el dispositivo habitual para escribir texto en el ordenador. Cada tecla presionada es una Esa señal de entrada (input) puede utilizarse para interactuar con las acciones programadas en el end 4.3. Eventos de teclado for x=50, OUTPUT_W-50, 20 do end function update() end function draw() ga.background(0.0,1.0) end function keyReleased() end ga.waveFrequency(0,(ga.key()/3)*ga.nToF(RE_0)) end function mouseReleased() if option >= 5 then ga.waveVolume(0,of.randomuf()/4) end option = 1 else -- cada vez que se suelta el botón del ratón option = option + 1 -- va sumando 1 a la variable option end end 152 153