herramientas‎ > ‎

Fluxus

*Nota: La siguiente 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.

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


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.

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

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







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







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


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



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)















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

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.





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





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


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  tvector de 3 para u y v, el tercer numero es ignorado
color de los vértices
 cvector 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.
(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.
(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.
(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.
(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

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


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)

ej. (define cilindro (build-cylinder 10 10))

(define (a)
    (with-primitive cilindro
        (pdata-index-map!
            (lambda (indice p) (vadd p (vmul (vector (* 0.01 indice) 0 0)) 0.001))

(every-frame (a))

Donde indice es un nombre arbitrario que representa los índices de cada uno de los vértices del primitivo, empezando por el 0 y terminando en el total de vértices.

Pdata-fold

Es una estructura de control de alto nivel para hacer cálculos sobre arreglos de pdata. Corre un procedimiento sobre cada unos de los elementos del pdata acumulando el resultado.

(pdata-fold procedimiento valor-de-partida leer-nombre-de-pdata)


ej. (define torus (build-torus 1 2 30 30))
(let ((centro
        (with-primitive torus
                    (vdiv (pdata-fold vadd (vector 0 0 0) "p") (pdata-size)))))
(display centro (newline))

Este ejemplo encuentra el centro de un primitivo promediando la posición de todos los vértices.


Pdata-index-fold

(pdata-index-fold procedimiento valor-de-partida leer-nombre-de-pdata)

Lo mismo que pdata-fold, excepto que pasa por el índice del pdata actual como primer argumento del procedimiento.


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


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     svector 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              w número

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

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




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

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


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ónp
vector de 3
 resistencia (strength)
(radio de las influencias)
snúmero
 colorc vector de 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)


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

Pdata de usuario

Así como hay información estándar que contienen los primitivos , también podemos incorporar nuestra propia información para cada vértice (pdata de usuario), que puede ser escrito y/o leído de la misma manera que los tipos de pdata que ya conocemos.

Para crear una nueva información del primitive (pdata) utilizamos el comando
(pdata-add nombre tipo).

Donde nombre es un string con el nombre como queremos que se llame nuestro pdata; tipo es otro string de un caracter que consiste en:

f: flotante
v: vector
c: color
m: matriz

ej.
(clear)
(define esfera (build-sphere 100 100))
    (with-primitive esfera
    (pdata-add "def" "v") ; nuestro pdata se llama "def" y es de tipo vector.
    ;establecemos la información de "def"
    (pdata-map! (lambda (def) (vmul (hsrndvec) 0.1)) "def"))
   
(define (deformar)
    (with-primitive esfera
    ;cada cuadro "p" (pdata de posición) se suma a "def"
    (pdata-map! vadd "p" "def")))
(every-frame (deformar)

hsrndvec devuelve un vector aleatorio, cuyos puntos al ser visualizados en gran cantidad forman una esfera centrada en el origen.

(pdata-copy fuente destino) copia un array de pdata, o sobre escribe un pdata existente.

ej.
(clear)
(define esfera (build-sphere 100 100))
    (with-primitive esfera
    (pdata-add "def" "v")
    (pdata-map! (lambda (def) (vmul (srndvec) 5)) "def")
    (pdata-copy "def" "p")) ; "def" sobre escribe sobre "p"

Operaciones de Pdata

Las operaciones de pdata son optimizaciones. Estas hacen que la deformación de un primitivo sea más rápida, además de simplificar el código.

(pdata-op operación pdata operando)
Donde operación es un string que representa la operación a realizar, pdata es el nombre del pdata que vamos a operar y el operando puede ser un número, o un vector (de 3, 4 o 16) o el nombre de otro arreglo pdata.

Las operaciones pdata son:
"+" : suma
"*" : multiplicación
"sin" : seno

ej:
(clear)
(define esfera (build-sphere 100 100))
    (with-primitive esfera
    (pdata-add "def" "v")
    (pdata-map! (lambda (def) (vmul (hsrndvec) 0.1)) "def"))
   
(define (deformar)
    (with-primitive esfera
    (pdata-op "+" "p" "def"))) ; "+" en lugar de "vadd"
(every-frame (deformar)


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





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






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


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



Instancing

Algunas veces puede ser complicado manejar primitivos construidos con el comando (build-*), también llamado retained mode, por ejemplo si queremos crear muchos objetos idénticos o hacer recursividad donde estamos llamando al mismo primitivos en muchos estados diferentes . Para simplificar estos procesos podemos utilizar:

(draw-instance mi-objeto)

Este comando hará una copia del objeto construido con el comando (build-*) pero en estado inmediato -immediate mode-, lo que permitirá manipularlo de diferente manera.

ej.
(define esfera (build-nurbs-sphere 8 10)) ; carga una esfera en modo fijo (retained mode)

(define (muchas-esferas n)
    (cond ((not (zero? n))
        (with-state
                (translate (vector n 0 0))
                (draw-instance esfera)) ; hace una copia de la esfera ahora en modo inmediato                                        (muchas-esferas (- n 1)))))

(every-frame (muchas-esferas (- n 1))

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)










Herramientas       Proyectos       Actividades      Miembros        Documentación 
                                                      menu principal
 
Comments