Taller de Audio del Centro Multimedeia del CNA

Fluxus

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


- Esta es la documentación utilizada en el curso Introducción a ambientes de animación en tiempo real (fluxus) impartido en el Centro Multimedia del Centro Nacional de las Artes.

Bienvenida


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.

fluxus_inicio

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.


Un vistazo rápido a 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.


Comandos con el teclado (shortcuts)


  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.



Control de cámara

camara
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

Haciendo algunas figuras (primitivos).


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)


Transformación


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))


cubo



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


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))


ejemplo1

ej 2.

(clear)
(define (estados)
    (colour (vector 0 0.5 1))
    (colour (vector 1 0.5 0)) ; color naranja
    (draw-cube))

(every-frame (estados))

ejemplo2



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))

ejemplo1.2



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))

ejemplo2.2

Animación


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))


FFT o Fast Fourier Transform


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.

Configuración de Jack para fluxus en Linux

Para que fluxus sea reconocido por Jack, debemos configurarlo.El siguiente link nos muestra este proceso, Pawfal: FluxusInfoSpanish.

Configuración de Jack para fluxus en Mac

*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.

FFT con fluxus y SuperCollider


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.


OSC (Open Sound Control)


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.

OSC con fluxus y SuperCollider


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.

Let


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))

Let

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.


Haciendo más Primitivos (build-*)


Con la función (build-*) creamos primitivos sin necesidad de definirlos, sin embargo, hay algunos que requieren de argumentos.



- (build-cube)

ej. (build-cube)



cuboo


- (build-sphere segmentos-horizontales                  segmentos-verticales)

ej. (build-sphere 10 10)

build-sphere

- (build-torus radio-interno radio-externo segmentos-horizontales segmentos-verticales)

ej. (build-torus 1 2 20 20)

build-torus

- (build-cylinder segmentos-horizontales segmentos-verticales)

ej. (build-cylinder 5 5)

build-cylinder

- (build-plane)

ej. (build-plane)

build-plane


ej 1. (build-seg-plane)??

Transformación con (build-*)


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.

transf_torus



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))
tranf_torus_plane-verde

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)))


transf_cilindro2


Animación con (build-*)


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))

anim_esfer

Deformación de Primitivos (pdata)


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)))


vertnum


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)))

pdataset_torus

(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))
pdata-ref_cubo
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)))


pdata_normals

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)))

pdat_texture


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)))

pdata-set color

Lambda


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)

pdata_map_p_c

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"))

pdata_map_t_n

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))


Pdata-index-map!


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.??


Primitivos que requieren del pdata


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


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)))
nurbs-sphere

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)))
nurbs_plane2

Partículas


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"))

Ribbon


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"))

ribbon

Text

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))

text_prim


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

Type

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"))

type

Locator

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)

Pixel

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)))
pixeles

Blobby


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

ej.

(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)

blobby

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)))

Recursividad

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.

Recursividad utilizando For


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))


recursividad_cubo123


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))


recursividad_for_inrange



Condicionales

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))

recursividad_for_inrange

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

cond repl


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

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

If

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

Predicados Compuestos


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))

recurividad


Materiales


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)

Modificadores de la luz:


(specular v)
(ambient v)
(emissive v)
(shyniness n)

Opacidad:
(opacity n)

Fluxa

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.

(pixel-primitive ancho alto)