Fluxus es un ambiente de
programación para la realización de
animación y audio en tiempo real.
Su modificación es constante permitiendo que el acto fluya,
de ahí su nombre flux (cambio).
Su interfaz facilita la realización de "live coding", que es
el programar en vivo, lo que lo convierte en una herramienta expresiva
característica del performance escénico.
Utiliza
Scheme, un lenguaje de programación muy flexible y
elegante.
Los
siguientes son videos de fluxus http://www.vimeo.com/357392.
Fluxus se puede descargar desde http://www.pawfal.org/fluxus/packages/.
Lista de referencia de los comandos de fluxus: Lista de comandos de
fluxus
Lo primero que vemos al iniciar fluxus es una pantalla negra con un
mensaje de bienvenida, una indicación para la ayuda y un prompt.
Este es el Repl (read evaluate print
loop) que es la interfaz donde se pueden hacer pruebas rápidas y
se muestran los errores de un script, además por supuesto de ser
la ventana de ayuda.
Para accesar a esta ventana usamos ctrl+0.
Para ayuda escribimos (help) que nos desplegará una serie de
tópicos.
Para buscar un tópico en específico escribimos (help
"nombre-del-tópico")
ej.- (help "authors") ; los autores de fluxus.
Fluxus es un editor de texto. Tenemos 9 hojas (también llamados
workspaces) para escribir nuestros códigos, sin embargo solo se
puede ejecutar una hoja a la vez. Con ctrl+1-9 podemos cambiar de hoja.
Con F5 o ctrl+e ejecutamos el programa visualizando el
resultado inmediatamente.
ctrl-f : Ver pantalla completa.
ctrl-w : Bajar el código a la esquina inferior izquierda.
ctrl-h : esconde/muestra el código
ctrl-l : Cargar un Script (anteriormente guardado)
ctrl-s : Salvar Script
ctrl-d : Salvar el script actual con un nombre (Save as).
ctrl-1 to 9 : Pasar de un Workspace a otro.
ctrl-0 : Ir a la pantalla Repl.
ctrl-q : limpiar Repl
ctrl-p : Le da formato al Script haciéndolo
más fácil de leer y más estético.
F3 : Reinicia la cámara.
F4 : ejecuta la expresión seleccionada.
F5/ctrl-e : Ejecuta el texto seleccionado o todo el Script.
F6 : Reinicia la interpretación y ejecuta el texto.
F9 : Randomise the text colour (aka the panic button).
F10 : Hace el texto más transparente.
F11 : Hace el texto menos transparente.
En fluxus tenemos una cámara que es donde vemos los objetos que
hemos diseñado. Con el mouse podemos manejar la cámara.
Botón izquierdo: rotar
Botón de en medio: mover
Botón derecho: zoom
Fluxus funciona con la librería OpenGL, que permite realizar
gráficos en 2D Y 3D. Hay figuras (también llamados
primitivos) ya precargadas que permiten traerlas al editor de manera
inmediata con el comando (draw-*). Estas figuras son cube, sphere,
plane, torus y cylinder. El siguiente código muestra como
renderear una figura en este modo inmediato.
(define (mi-figura)
(draw-cube))
(every-frame (mi-figura))
Usamos define para asignarle un nombre a nuestra función,
(mi-figura) es el nombre y el comando draw-cube es el cuerpo de
la función, con el que le diremos que traiga nuestra figura a la
pantalla.
Fluxus funciona como una cámara de video que esta corriendo todo
el tiempo desde que abrimos el programa, incluso sin haber ejecutado
nada, esto quiere decir que hay cierta cantidad de cuadros por segundo
corriendo. El comando every-frame le dice a fluxus que queremos que
nuestro cubo aparezca en todos los cuadros y es por eso que al
ejecutarlo veremos un cubo estático.
La forma general de una función es,
(define (<nombre> <parámetros formales>)
<cuerpo>).
ej. (define (el-cuadrado-de x) (* x x)); esto se leería de la
siguiente manera:
Hacer el-cuadrado-de algo,
multiplicar ese algo por sí mismo.
* En scheme los operadores se escriben antes que los operandos.
ej. (* 2 3)
ej. (+ 5 2)
ej. (/ 10 2)
Para transformar nuestro programa podemos hacer lo siguiente;
(define (mi-figura)
(colour (vector 1 0 0))
(scale (vector 4 4 4))
(translate (vector 1 1 0))
(rotate (vector 45 45 0))
(draw-cube))
(every-frame (mi-figura))
Ahora el cubo es de color rojo, es más grande, se ha trasladado
un poco sobre el eje "x" y otro poco sobre el eje "y" y ha rotado
45° alrededor de ambos ejes.
El comando (colour) necesita un vector de 3 números y funciona
por medio de rgb, esto quiere decir que el primer número es rojo
(red), el segundo verde (green) y el tercero azul (blue).
El comando (scale) requiere de un vector de 3 números, donde el
primero es "x", el segundo "y" y el tercero "z".
El comando (translate) requiere de un vector de 3 números ,
donde el primero es "x", el segundo "y" y el tercero "z".
El comando (rotate) requiere de un vector de 3 números que son
los grados en x, y, z. Es importante mencionar que el primitivo va a
rotar alrededor de los ejes y no sobre ellos.
* Ahora puedes probar cambiar el cubo por otra de las figuras ya
precargadas.
Texturas
El formato que fluxus acepta para las texturas es PNG.
Por default fluxus tiene una carpeta donde vienen imágenes que
podemos usar para hacer pruebas y donde podemos guardar imágenes
propias.
Esta carpeta se encuentra donde se instaló fluxus y generalmente
se encuentra en /usr/share/fluxus-017/material/textures en linux y en
/Applications/fluxus/Fluxus.app/Contents/Resources/material/textures en
Mac.
El comando para insertar una textura es (texture id-de-la-textura).
ej.
(define (esfera)
(texture (load-texture "test.png"))
(draw-sphere))
(every-frame (esfera))
Para cargar una imagen desde una carpeta que no este predeterminada en
fluxus, colocamos la ruta.
ej.
(define (esfera)
(texture (load-texture
"/home/groucho/Desktop/grecia.png"))
(draw-sphere))
(every-frame (esfera))
La primera vez que se carga una textura es desde el disco duro, pero
las subsecuentes serán desde la memoria cache, entonces si
modificamos una textura con algún programa como Gimp y
después lo guardamos, el cambio no se verá en fluxus a
menos que volvamos a tomar la imagen desde el disco duro. Para hacer
esto utilizamos (clear-texture-cache).
Multitexturas
Fluxus permite aplicar más de una textura a cada primitivo e
incluso se pueden combinar texturas.
Podemos cargar hasta 8 texturas a la vez, utilizando:
(multitexture número-de-la-textura textura)
El número de unidad por default es 0,
ej.- (clear)
(define (esfera)
(multitexture 0 (load-texture "test.png"))
(multitexture 1 (load-texture
"home/groucho/Desktop/grecia.png"))
(draw-sphere))
(every-frame (esfera))
La máquina de estados es la manera como funciona Fluxus. Esto
significa que cada línea de comandos va a influir sobre la que
siga, por lo tanto, si hay algún error o incongruencia en
nuestro script lo que sigue no se ejecutará hasta que el error
sea corregido.
La máquina de estados también se refiere a las
características que se le asignan a nuestros primitivos, y que
cambian cuando le damos otros parámetros, por ejemplo, si
primero tenemos un cubo azul y después le decimos que ahora sea
naranja.
ej 1.
(clear)
(define (estados)
(colour (vector 0 0.5 1)) ; color azul
(draw-cube))
(every-frame (estados))
ej 2.
(clear)
(define (estados)
(colour (vector 0 0.5 1))
(colour (vector 1 0.5 0)) ; color naranja
(draw-cube))
(every-frame (estados))
Los estados de un primitivo se pueden encasillar para que no afecten a
otros primitivos, para eso usamos (with-state).
ej 1.
(clear)
(define (estados)
; el color azul afecta a los
dos cubos
(colour (vector 1 0 0))
(draw-cube)
(translate (vector 2 0 0))
(draw-cube))
(every-frame (estados))
ej 2. Ahora cada cubo es independiente
(clear)
(define (estados)
(colour (vector 1 0 0))
(draw-cube)
(with-state
(colour (vector 1 0 0))
(translate (vector 2 0 0))
(draw-cube)))
(every-frame (estados))
Una forma de animar nuestro primitivo es usando el comando (time), que
devuelve el tiempo en segundos desde que fluxus fue abierto, es decir,
devuelve un número flotante que se actualiza una vez por cuadro.
Por sí sólo este comando no es muy útil, pues el
cubo girará muy lento al iniciar el programa y muy rápido
si el programa lleva mucho tiempo abierto, entonces hay que pasarle una
onda senoidal que nos dará un valor de - 1 a 1, para mantener
controlado el movimiento de la figura.
(define (mi-figura)
(rotate (vector (* 45 (sin (time))) 0 0));
rotará 45° constantemente
(draw-cube))
(every-frame (mi-figura))
Otra forma de animar nuestros primitivos es por medio de FFT (
Transformada Rápida de Fourier).
Esto quiere decir que fluxus convierte la señal de audio
entrante a frecuencias armónicas que podemos utilizar por medio
del comando (gh).
Hay 16 bandas armónicas disponibles de 0-15, donde 0 recibe las
frecuencias más graves y 15 las más agudas.
Para recibir señal de audio usamos el Jack Control que es un
conjunto de herramientas para conectar dispositivos MIDI y de Audio,
tanto de hardware como de software.
Para que fluxus sea reconocido por Jack, debemos configurarlo.El
siguiente link nos muestra este proceso, Pawfal:
FluxusInfoSpanish.
*nota: es importante considerar que para el sistema operativo TIger
debemos usar jack pilot 0.81, para el SO 10.5 la versión 0.80
ó 0.85 de jack Pilot y para Snow Leopard la versión de 32
bits del Jack 0.85.
1) Abrimos Jack Pilot
2) En la ventana de preferencias verificamos lo siguiente:
Driver: Coreaudio
Input Device: built-in Input
Output Device: built-in output
sample rate: 44100
buffer size: 1024
3) Abrimos fluxus y en un workspace nuevo (ctrl+1...9) escribimos el
siguiente código:
(clear)
(start-audio "system:capture_1" 1024 44100)
(define (armonicos)
(colour (vector (gh 0) (gh 1) (gh 2)); los colores
responden a las bandas de armónicos
(draw-cube))
(every-frame (armonicos))
*Nota: Es importante mencionar que el comando (gh) que es una
abreviatura de get-harmonic, no tiene ninguna relación con los
armónicos naturales. Así mismo, las bandas de frecuencia
que fluxus toma no giran alrededor de algún armónico en
particular.
Para enviar señales de audio desde SuperCollider a fluxus
debemos:
1) Abrir Jack
2) Encender Jack (con la configuración de Jack antes
mencionada).
3) Abrir fluxus y en un workspace nuevo (ctrl+1...9) escribir el
siguiente código:
(clear)
(start-audio "" 1024 44100); permite que fluxus sea reconocido por Jack.
3) En Jack damos click en conectar, que mostrará una venta con
los puertos de entrada y de salida.
4) Seleccionar Fluxus y SuperCollider y dar click en conectar.
Es un protocolo estándar que permite la comunicación
entre programas de arte, instrumentos musicales y computadoras. Fue
pensado para compartir información musical en tiempo real sobre
una red.
Fluxus puede enviar y recibir mensajes OSC.
Para recibir mensajes OSC desde SuperCollider hacemos lo siguiente en
SuperCollider:
1) Establecer el ip de la computadora a la que enviaremos el mensaje.
2) Establecer el puerto de entrada y de salida.
3) Establecer el "tag" o mensaje.
ej:
s.boot
~fluxus=NetAddr("192.168.5.189", 4567)
Tdef(\osc, {inf.do{~fluxus.sendMsg("/zzz", 15,16,17);
0.02.wait}})
Tdef(\osc).stop
Tdef(\osc).play
Donde "192.162.5.189" es el ip de la computadora a la que se le
mandarán mensaje OSC. 4567 es el puerto de entrada y de salida,
y es un puerto arbitrario al que se le puede asignar un número
designado por nosotros. "/zzz" es el tag o mensaje.
En fluxus:
1) Establecer el puerto de entrada y salida (que debe ser el mismo que
en SuperCollider).
2) Establecer el "tag" o mensaje (que debe ser el mismo que en
SuperCollider).
ej:
(clear)
(osc-source "4567"); puerto de entrada
(define (prueba)
(with-state
(cond
((when (osc-msg "/zzz"); mensaje o tag
(rotate (vector (osc 0) (osc 1) (osc 2)))
(draw-cube)))
(every-frame (prueba))
El comando (osc 0) devuelve el primer valor del mensaje, y así
sucesivamente. Por tanto en el ejemplo, SuperCollider esta mandando la
lista de valores (15,16,17) y esto hará que en fluxus el cubo
rote 15° sobre x, 16° sobre y, 17° sobre z, equivalente a
(osc 0) (osc 1) (osc 2) respectivamente.
Para probar si esta llegando algún mensaje a fluxus, podemos
hacerlo con el siguiente código.
(clear)
(osc-source "4567")
(define (prueba)
(display (vector (osc 0) (osc 1) (osc 2))) (newline))
(every-frame (prueba))
Este código desplegará el primer, segundo y tercer valor
del mensaje OSC que esta llegando a fluxus y se visualizan en el Repl.
Se usa para crear variables locales. Esto quiere decir que cierta
información se va a guardar sólo en una sección
determinada de nuestro código.
ej.-
(clear)
(define (figuras)
(let ((x (sin (time))))
(with-state
(rotate (vector 0 (* 45 x) 0))
(colour (vector (* 5 x) 0 0))
(draw-torus))) ; aquí se cierra el paréntesis que se
abrió desde let
(with-state
(translate (vector 2.5 0 0))
(colour (vector 0 1 0.5))
(draw-cube)))
(every-frame (figuras))
Entonces la información contenida en let solo es válida
dentro de los paréntesis que la resguardan; en el ejemplo el
paréntesis de let termina después del comando
(draw-torus), por tanto no modifica el siguiente estado que es el del
cubo verde y si intentamos utilizar "x" en este estado, comprobaremos
que let solo funciona para el estado del torus.
Con la función (build-*) creamos primitivos sin necesidad de
definirlos, sin embargo, hay algunos que requieren de argumentos.
- (build-cube)
ej. (build-cube)
- (build-sphere
segmentos-horizontales
segmentos-verticales)
ej. (build-sphere 10 10)
- (build-torus radio-interno radio-externo segmentos-horizontales
segmentos-verticales)
ej. (build-torus 1 2 20 20)
- (build-cylinder segmentos-horizontales segmentos-verticales)
ej. (build-cylinder 5 5)
- (build-plane)
ej. (build-plane)
ej 1. (build-seg-plane)??
ej 1. No es necesario definir nuestra función, ni usar el
comando (every-frame).
(clear)
(colour (rndvec)) ; color aleatorio
(scale (vector 3 2 1))
(rotate (vector 0 45 0))
(translate (rndvec)) ; posición aleatoria
(build- torus 0.5 1 5 5)
; (rndvec) devuelve un vector de 3 números aleatorio.
ej 2. Con (with-state) se pueden encasillar los estados de nuestros
primitivos para que sean independientes.
(clear)
(with-state
(colour (rndvec))
(scale (vector 2 1 0.5))
(rotate (vector 0 45 0))
(build- torus 0.5 1 5 5))
(with-state
(rotate (vector 90 0 0))
(scale (vector 25 15 15))
(translate (vector 0 0 0.3))
(build-plane))
La función (build-*) devuelve un número de
identificación que se puede guardar para llamar y modificar el
primitivo después. La información de estos objetos se
llama primitive data o pdata.
Entonces, otra forma de hacer transformaciones es definir el primitivo
y después llamarlo con el comando (with-primitive).
ej 3.
(clear)
(define clindro (build-cylinder 1 15))
(with-primitive cilindro
(hint-none) (hint-wire) ; solo líneas
(line-width 10) ; grosor de la línea
(wire- colour (rndvec)) ; color de la línea
(scale (vector 4 4 4))
(rotate (vector 0 45 0)))
ej.
(clear)
(define esfera (build-sphere 10 10))
(with-primitive esfera
(scale 5))
(define (animar)
(with-primitive esfera
(hint-none) (hint-wire)
(wire-colour (rndvec))
(line-width (* 2 (sin
(time))))
(rotate (vector (delta)
(delta) 0))))
(every-frame (animar))
Otro comando es (delta), que regresa el tiempo en segundos desde el
último cuadro.
(define (mi-figura)
(rotate (vector (* 45 (delta)) 0 0)) ;
girará 45 grados por segundo.
(build-cube))
(every-frame (mi-figura))
El pdata es un arreglo (array) de información que contiene un
objeto. Los primitivos contienen varios arreglos pdata, todos del mismo
tamaño, y cada uno con un nombre.
Modificando el pdata podemos hacer deformaciones a los primitivos.
La siguiente tabla muestra los tipos de pdata disponibles para los
polígonos;
Uso | Nombre | Tipo de Información |
posición de los vertices | p | vector de 3 |
normal de los vértices | n | vector de 3 |
coordenadas de textura de los vértices | t | vector de 3 para u y v - el tercer numero es ignorado |
color de los vértices |
c | vector de 4 rgba |
Para modificar el pdata utilizamos el comando (pdata-set! nombre
número-vert vector).
Pdata-set!, establece la información del primitivo a un vector
de entrada.
Nombre, describe la información a la que queremos accesar, por
ejemplo "p", que contiene la posición de los vertices.
Número-vert, es el número de vértice que vamos a
modificar.
ej.
(clear)
(hint-wire)
(define esfera (build-sphere 20 10))
(with primitive esfera
(scale 3)
(pdata-set! "p" 600 (vector 1 2 0))
(pdata-set! "p" 601 (vector 1 1 0))
(pdata-set! "p" 602 (vector 2 2 0)))
ej 2.
(clear)
(hint-wire)
(hint-origin)
(define torus (build-sphere 20 10))
(with primitive torus
(scale 3)
(pdata-set! "p" 600 (vector -4 3.3 0))
(pdata-set! "p" 601 (vector -5 3 0))
(pdata-set! "p" 602 (vector -4.1 1 0))
(pdata-set! "p" 603 (vector -3 1.2 0)))
(pdata-ref nombre num-vector) devuelve el vector original del vertice.
ej.3
(clear)
(hint-wire)
(hint-points)
(point-width 10)
(backfacecull 0)
(hint-origin)
(define cubo (build-cube))
(with-primitive cubo
(scale 4)
(display "esta es la posición original de los
vertices 12, 13, 14, 15") (newline)
(display (pdata-ref "p" 12)) (newline)
(display (pdata-ref "p" 13)) (newline)
(display (pdata-ref "p" 14)) (newline)
(display (pdata-ref "p" 15)) (newline)
(pdata-set "p" 12 (vadd (pdata-ref "p" 12) (vector
-1 0 0)))
(pdata-set "p" 13 (vadd (pdata-ref "p" 13) (vector
-1 0 0)))
(pdata-set "p" 14 (vadd (pdata-ref "p" 14) (vector
-1 0 0)))
(pdata-set "p" 15 (vadd (pdata-ref "p" 15) (vector
-1 0 0)))
(display "esta es la nueva posicion") (newline)
(display (pdata-ref "p" 12)) (newline)
(display (pdata-ref "p" 13)) (newline)
(display (pdata-ref "p" 14)) (newline)
(display (pdata-ref "p" 15)) (newline))
Cada vértice de cada cara tiene una normal que es una
línea perpendicular. Esta nos sirve para los efectos de luz,
pues cuando una normal se mueve de su posición original afecta
el ángulo en que la luz es reflejada provocando una sombra.
ej.4
(clear)
(hint-normal); muestra la normal de los vértices
(define cubo (build-cube))
(with-primitive cubo
(scale (vector 3 3 3))
(pdata-set! "n" 15 (vector -7 0 1)))
ej.5
(clear)
(clear-colour (vector 0.5 0.5 0.5)) ; fondo gris
(hint-wire)
(define plano (build-plane))
(with-primitive plano
(scale 8)
(texture (load-texture "refmap.png"))
(pdata-set! "t" 0 (vector 8 0 0)))
ej. 6
(clear)
(clear-colour 0.5)
(define esfera (build-sphere 10 20))
(with-primitive esfera
(scale 5)
(pdata-set! "c" 600 (vector 1 0 0))
(pdata-set! "c" 601 (vector 1 0 1))
(pdata-set! "c" 602 (vector 0 1 0)))
Lambda se usa para crear procedimientos de la misma forma de define,
excepto que no se especifica un nombre para el procedimiento:
(lambda (<parámetros-formales>) <cuerpo>)
ej.
(define (suma x) (+ x 4))
4
es lo mismo que;
((lambda (x) (+ x 4) 1)
4
pdata-map!
Pdata-map! modifica cada elemento del pdata y escribe el resultado del
procedimiento dentro del primer nombre del array pdata.
(pdata-map! procedimiento leer/escribir-nombre-del-pdata
leer-nombre-del-pdata)
ej.
(clear)
(clear-colour 0.7)
(hint-vertcols)
(define cubo (build-cube))
(with-primitive cubo
(scale 5)
(pdata-map!
(lambda (p) (rndvec)); posición aleatoria
de
todos los vértices
"p")
(pdata-map!
(lambda (c) (rndvec))
"c"))
ej.
(clear)
(clear-colour 0.7)
(define cubo (build-cube))
(with-primitive cubo
(scale 5)
(rotate (vector 25 25 0))
(texture (load-texture "refmap.png")))
(define (animar)
(with-primitive cubo
(pdata-map!
(lambda (t) (vmul (rndvec) 50))
"t")
(pdata-map!
(lambda (n) (vmul (rndvec) 2))
"n")))
(every-frame (animar))
Es lo mismo que pdata-map! pero además proporciona el indice del
pdata actual como primer argumento al procedimiento.
(pdata-index-map! procedimiento leer/escribir-nombre-del-pdata
leer-nombre-del-pdata)
Se pueden hacer arreglos de pdata propios.??
Cuando usamos la función build se generan automáticamente
algunos arreglos pdata. Algunas veces estos arreglos generados
automáticamente resultan en un primitivo que podemos usar de
inmediato en comandos como (build-cube), pero otros primitivos
sólo son funcionales si el pdata es establecido y controlado por
nosotros.
NURBS es un modelo matemático utilizado en la computación
gráfica para generar y representar curvas y superficies. Se
manejan de manera similar a los polígonos, excepto que los
elementos de pdata representan el control de los vértices de la
curva.
La siguiente tabla muestra los tipos de pdata disponibles para los
NURBS:
Uso | Nombre | Tipo de Información |
Control de la posición de vértice | p | vector de 3 |
Control de la normal del vértice | n | vector de 3 |
Control de las coordenadas de la textura del vértice | t | vector de 3, para u y v. El tercer número es ignorado |
ej.
(clear)
(define nurb (build-nurbs-sphere 10 20))
(with-primitive nurb
(scale 3)
(colour (vector 1 2 1))
(pdata-set "p" 95 (vector -1 1 1)))
ej.
(clear)
(define plano-nurb (build-nurbs-plane 10 10))
(with-primitive plano-nurb
(scale 5)
(texture (load-texture "test.png"))
(translate (vector 1 0
0))
(pdata-set "t" 50 (vector 1 1 0)))
Las partículas usan los elementos de pdata para representar un
punto. Este primitivo es útil para efectos como agua, humo,
nubes y explosiones.
(build-particles num-particles)
El pdata disponible es:
Uso | Nombre | Tipo de Información |
posición de la partícula | p | vector de 3 |
Color de la partícula | c | vector de 3 |
Tamaño de la partícula | s | vector de 3 para ancho y alto, el tercer numero es ignorado |
ej.
(clear)
(define particulas (build-particles 50))
(with-primitive particulas
(pdata-map!
(lambda (p) (rndvec)) ; posición aleatoria de
las partículas
"p"))
Este primitivo dibuja una línea que conecta cada elemento del
pdata de los vértices.
(build-line num-puntos)
El Pdata disponible es;
Uso | Nombre | Tipo de Información |
Posición del vértice del listón | p | vector de 3 |
Color del vértice del listón | c | vector de 3 |
Ancho del vértice del listón | s | numero |
ej.
(clear)
(hint-unlit)
(with-primitive linea
(texture (load-texture "refmap.png"))
(pdata-set! "p" 0 (vector 0 0 0))
(pdata-set! "p" 1 (vector 2 0 3))
(pdata-set! "p" 2 (vector 0 3 1))
(pdata-set! "p" 3 (vector 1 6 0))
(pdata-map! (lambda (w) 0.3) "w"))
Este primitivo permite crear texto basado en fonts de textura.
(texture (load-texture "font.png"))
(build-text text-string)
ej.
(clear)
(texture (load-texture "font.png"))
(define hola (build-text "hola"))
(with-primitive hola
(scale 2.5))
NOTA: en las texturas que vienen en la carpeta de archivos de fluxus
debería venir
este "font.png", sin embarho no es así. Para hacer una prueba el
font.png se puede
descargar desde esta página
http://www.pawfal.org/flotsam/betablocker/font.png
El primitivo type provee una mayor calidad de rendereado que el
primitivo text. Este usa un font ttf. También se puede extruir
el texto, lo que resulta en formas 3D.
(build-type nombre-del-font-ttf texto)
(build-extruded-type nombre-del-font-ttf texto profundidad-del-extruido)
(type->poly nombre-del-primitivo-type); convierte el primitivo type
a un polígono con una lista de triángulos. Esto permite
que se le pueda hacer todo lo que a un polígono, como agregar
texturas, deformarlo, etc.
ej. (clear)
(define fluxus (build-extruded-type "roman.ttf" "fluxus" 7))
(with-primitive fluxus (hide 1)) ; esconde el primitivo fluxus
(define nuevo-fluxus (type->poly fluxus))
(with-primitive nuevo-fluxus
(scale 0.7)
(translate (vector -8 0 0))
(rotate (vector 20 -25 0))
(texture (load-texture "refmap.png"))
(pdata-map!
(lambda (t p) (vmul p 0.5)) "t" "p"))
El primitivo locator no renderea nada realmente. Son útiles
para reconocer donde esta el orígen de otros primitivos o para
construir esqueletos. Para hacerlos visibles usamos (hint-origin).
ej.
(hint-origin)
(build-locator)
ej.
(hint-origin)
(build-cube)
Este primitivo es usado para hacer texturas procesales, que se
pueden aplicar a otros primitivos. Por esta razón el primitivo
pixel no se renderea tan directamente, aunque podemos hacerlo para
visualizarlos.
(pixel-primitive ancho alto)
Tipos de pdata disponibles:
Uso | Nombre | Tipo de Información |
Color del pixel | c | vector de 3 |
alpha del pixel | a | numero |
ej.
(clear)
(define pixeles (build-pixels 128 128))
(with-primitive p
(scale 0); escondemos el plano con pixeles
(pdata-map!
(lambda (c)
(rndvec))
"c")
(pixels-upload))
(define torus (build-torus 1 2 10 10))
(with-primitive torus
(texture (pixels->texture pixeles)))
Un blobby es la representación de una superficie
implícita que es definida usando campos de influencia en el
espacio. Es decir, es una superficie que no esta definida por
vértices sino matemáticamente.
Estos campos de influencia se suman para generar una superficie suave
(utilizando el algoritmo de los cubos que marchan (Marching cubes
algorithm)).
Los campos de influencia pueden ser animados y la superficie suave se
moverá y deformará para adaptarse.
(build-blobby numinfluences subdividionsvec boundingvec)
(build-blobby) devuelve un número de identificación para
el blobby.
Numinfluences es el número de "blobs".
Subdivisions permite controlar la resolución de la superficie en
cada dimensión.
Boundingvec establece el límite del área del primitivo en
espacio local del objeto. El primitivo no será calculado fuera
de esta área.
La posición y color de los campos de influencia se establece
usando pdata-set!.
Uso | Nombre | Tipo de Información |
posición | p | vector 3 |
resistencia (strength) (radio de las influencias) | s | número |
color | c | vector 3 |
(clear)
(define b (build-blobby 5 (vector 30 30 30) (vector 1 1 1)))
(with-primitive b
(shinyness 100); regula el brillo del primitivo
(specular (vector 1 1 1)); refleja la luz como un
espejo
(hint-vertcols); activa los colores
(scale 5)
(pdata-set! "p" 0 (vector 0.75 0.25 0.5))
(pdata-set! "c" 0 (vector 0.01 0 0))
(pdata-set! "s" 0 0.01); radio de las influencias
(pdata-set! "p" 1 (vector 0.25 0.75 0.5))
(pdata-set! "c" 1 (vector 0 0.01 0))
(pdata-set! "s" 1 0.01); radio de las influencias
(pdata-set! "p" 2 (vector 0.75 0.75 0.5))
(pdata-set! "c" 2 (vector 0.01 0.01 0))
(pdata-set! "s" 2 0.01); radio de las influencias
(pdata-set! "p" 3 (vector 0.5 0.5 0.5))
(pdata-set! "c" 3 (vector 0.01 0.01 0.01))
(pdata-set! "s" 3 0.025)); radio de las influencias
(blobby->poly nombre-del-primitivo)
Convierte la estructura del blobby a una lista de triángulos
como la de los polígonos. Esta función es útil
pues será más rápido de renderear, pero no nos
permitirá deformar de la misma manera que en e ejemplo anterior.
Una desventaja es que no convierte los colores de los vértices.
ej.
(define p (with-state
(translate (vector 1 0 0))
(blobby->poly b)))
La recursividad es cuando un procedimiento se llama a sí
mismo cierta cantidad de veces, manteniendo la cuenta de las veces que
esto sucede y termina después de varias iteraciones.
For es una forma sintáctica que permite la iteración
sobre secuencias permitiendo indicar el número máximo de
iteraciones. Estas secuencias pueden ser listas, vectores, strings,
byte strings, puertos de entrada y tablas de hash, también
utiliza constructores como in-range que permiten más tipos de
secuencias.
Su forma general es:
(for ([id sequence-expr] ...) cuerpo ...+)
ej.
(clear)
(define (cubo-recursivo)
(for ([i '(1 2 3)])
(translate (vector 1.5 1.5
0))
(draw-cube)))
(every-frame (cubo-recursivo))
La función in-range genera una secuencia de números,
dándonos un número de partida opcional (0 por default),
otro número donde termina la secuencia y otro número
opcional para avanzar (1 por default).
ej.
(clear)
(define (cubo-recursivo)
(for ([i (in-range 0 3)])
(translate (vector 1.5 1.5
0))
(draw-cube)))
(every-frame (cubo-recursivo))
Otra forma de hacer recursividad es tomando decisiones para saber
cuando detener las iteraciones. Utilizamos condicionales para esto.
ej.-
(define (dibuja-cubos cuenta)
(cond
((not (zero? cuenta))
(draw-cube)
(translate (vector 1.1 0 0))
(dibuja-cubos (- cuenta 1)))))
(every-frame (dibuja-cubos 10))
Los Condicionales se usan para hecer preguntas, y se pueden preguntar
tantas como querrámos -fluxus las revisa en orden y hace
la primera que es verdad-. En el ejemplo anterior estamos preguntando
(not (zero? cuenta)) -si "cuenta" es cualquier cosa diferente a cero,
entonces dibuja un cubo, muévelo un poco y luego vuelve a
empezar otra vez. Si la "cuenta" es cero, entonces el
procedimiento se detiene.
La forma general de una condicional es:
(cond (<p1> <e1>)
(<p2> <e2>)
.
.
.
(<pn>
<en>))
Al símbolo cond le siguen pares de expresiones dentro de un
paréntesis (<p> <e>) llamadas cláusulas. La
primer expresión en cada uno de estos pares se llama predicado.
El predicado es una expresión cuyo valor es interpretado como
verdadero o falso.
Las expresiones condicionales se evalúan de la siguiente manera.
El predicado <p1> es evaluado primero. Si su valor es "falso",
entonces <p2> es evaluado. Si el valor de <p2>
también es falso, entonces <p3> es evaluado. Este proceso
continua hasta encontrar un predicado cuyo valor sea "verdadero", en
cuyo caso el interprete devolverá el valor de la
expresión consecuente correspondiente <e> a la
cláusula, como el valor de la expresión condicional. Si
ninguno de los <p> es verdadero, el valor de la cond será
indefinido.
La palabra predicado se usa para procedimientos que devuelven verdadero
o falso, así como para expresiones que evalúan si es
verdadero o falso.
ej. Este ejemplo lo tenemos que correr en el repl.
(define (hola x)
(cond ((> x 0) x)
((=
x 0) 0)
((< x 0) - x)))
(hola 7); devuelve x
(hola 0); devuelve 0
(hola -5); devuelve -x
En el ejemplo anterior el procedimiento (hola x) hace uso de los
predicados primitivos >,<, =.
Entonces, toma dos números como argumentos y verifica si el
primer número, es respectivamente, mayor que, menor que o igual
al segundo número, devolviendo "verdadero" o "falso",
según sea el caso.
Else, se usa en vez de <p> en la cláusula final de una
cond. Esto causa que la cond tome el valor de la correspondiente
<e>, siempre y cuando las cláusulas anteriores hayan sido
evaluadas y descartadas.
ej. para correr en el repl
(define (hola x)
(cond ((< x 0) - x) ; si x es menor que 0
devuelve -x
(else x))) ; de otra manera devuelve x
La forma especial if, es tipo de condicional que se usa cuando hay
únicamente dos casos para analizar. La forma general de la
expresión if es;
(if <predicado> <consecuencia> <alternativa>)
ej.
(define (hola x)
(if (< x 0) ; si x en menor que 0
0 ; devuelve 0
(x)) ; de otra manera
devuelve x
Además de los predicados primitivos como <,=,> existen
operaciones que nos permiten construir predicados compuestos.
Los más comunes son:
(and <e1> ... <en>)
Se evalúa una expresión <e> a la vez, de izquierda
a derecha. Si algún <e> es falso, el valor de la
expresión and será falso y el resto de las <e> no
serán evaluadas. Si todas las <e> son verdaderas el valor
de la expresión and será el valor de la última
<e>.
ej.
(and (> x 5) (< x 10))
(or <e1> ... <en>)
Se evalúa una expresión <e> a la vez, de izquierda
a derecha. Si algún <e> es verdadero, el valor es devuelto
como el valor de la expresión or y el resto de las <e> no
son evaluadas. Si todas las <e> son falsas, el valor de la
expresión or será falso.
ej. (define (mayor-igual x y)
(or (> x y) (= x y)))
(not <e>)
El valor de una expresión not es verdadero cuando <e> es
falso y viceversa.
ej. (define (mayor-igual x y)
(not (< x y)))
ej.
(define (dibuja-cubos cuenta)
(cond
((not (zero? cuenta))
(translate (vector 2 0 0))
(draw-cube)
(rotate (vector 0 25 0))
(dibuja-cubos (- cuenta 1)))
(with-state
(rotate (vector 0 -25 0))
(dibuja-cubos (- cuenta 1)))))))
(every-frame (dibuja-cubos 10))
Otras formas de cambiar la apariencia es por medio de los
parámetros de la superficie.
Modificadores de Líneas y puntos:
(wire-colour n)
(line-width n)
(point-width n)
(specular v)
(ambient v)
(emissive v)
(shyniness n)
Opacidad:
(opacity n)
Fluxa es un una herramienta opcional que permite hacer sintesis de audio, así como cargar pistas. También es un sintetizador experimental y no determinista.
Fluxa permite construir y secuenciar sonido. Utiliza un estilo minimalista y funcional diseñado para el livecoding.
Para encender fluxa:
1) Abrir jack
2) Escribir "fluxa" en una terminal y dar enter.
3) Abrir fluxus
4) En una hoja nueva escribir (require fluxus-017/fluxa), en la sección fluxus-017 podemos cambiar la version segun sea el caso,
ej. fluxus-016 ó fluxus-018, etc.