6.1.6  Funciones Definidas por el Usuario

Algunos objetos permiten especificar funciones que serán evaluadas durante la renderización para determinar la superficie de estos objetos. En este aspecto las funciones son bastante diferentes de las macros, que se evalúan durante el tiempo de análisis (parse time) pero no afectan de ninguna otra manera a la renderización. Adicionalmente, se puede llamar a estas funciones en cualquier lugar en el que se permita utilizar una función en coma flotante, incluso durante el análisis. La sintaxis es idéntica a las expresiones en coma flotante, sin embargo sólo pueden usarse funciones en coma flotante que se apliquen a valores en coma flotante. Se excluyen por ejemplo strlen o vlength. Encontrarás una lista completa de funciones en coma flotante soportadas en la definición de sintaxis que se describe a continuación.

FLOAT:
    LOGIC_AND [OR LOGIC_AND]
OR:
    |
LOGIC_AND:
    REL_TERM [AND REL_TERM]
AND:
    &
REL_TERM:
    TERM [REL_OPERATOR TERM]
REL_OPERATOR:
    < | <= | >= | > | = | !=
TERM:
    FACTOR [SIGN FACTOR]
SIGN:
    + | -
FACTOR:
    MOD_EXPRESSION [MULT MOD_EXPRESSION]
MULT:
    * | /
EXPRESSION:
    FLOAT_LITERAL         |
    FLOAT_IDENTIFIER      |
    FLOAT_FUNCTION        |
    FLOAT_BUILT-IN_IDENT  |
    FUNCTION_IDENTIFIER   |
    ( FLOAT )             |
    IDENTIFIER            |
    SIGN EXPRESSION
FLOAT_FUNCTION:
    abs( FLOAT ) | acos( FLOAT ) | acosh( FLOAT ) | asin( FLOAT ) |
    asinh( FLOAT ) | atan( FLOAT) | atanh( FLOAT) |
    atan2( FLOAT , FLOAT ) | ceil( FLOAT ) | cos( FLOAT ) |
    cosh( FLOAT ) | degrees( FLOAT ) | exp( FLOAT ) |
    floor( FLOAT ) | int( FLOAT ) | ln (Float) | log( FLOAT ) |
    max( FLOAT , FLOAT, ... ) | min( FLOAT , FLOAT, ... ) |
    mod( FLOAT , FLOAT ) | pow( FLOAT , FLOAT ) |
    radians( FLOAT ) | sin( FLOAT ) | sinh( FLOAT ) |
    sqrt( FLOAT ) | tan( FLOAT ) | tanh( FLOAT ) |
    select( FLOAT , FLOAT , FLOAT [, FLOAT] )
FUNCTION_IDENTIFIER:
    #local FUNCTION_IDENTIFIER = function { FLOAT }               |
    #declare FUNCTION_IDENTIFIER = function { FLOAT }             |
    #local FUNCTION_IDENTIFIER = function(IDENT_LIST) { FLOAT }   |
    #declare FUNCTION_IDENTIFIER = function(IDENT_LIST) { FLOAT } |
    #local FUNCTION_IDENTIFIER = function{SPECIAL_FLOAT_FUNCTION} |
    #local VECTOR_IDENTIFIER = function{SPECIAL_VECTOR_FUNCTION}  |
    #local COLOR_IDENTIFIER = function { SPECIAL_COLOR_FUNCTION } |
IDENT_LIST:
    IDENT_ITEM [, IDENT_LIST]
IDENT_ITEM:
    x | y | z | u | v | IDENTIFIER
    (Note: x = u and y = v)
SPECIAL_FLOAT_FUNCTION:
    pattern { PATTERN_BLOCK }
SPECIAL_VECTOR_FUNCTION:
    TRANSFORMATION_BLOCK | SPLINE
SPECIAL_COLOR_FUNCTION:
    PIGMENT
PATTERN_BLOCK:
    PATTERN

Nota: Sólo los objetos mencionados arriba pueden utilizarse en las funciones definidas por el usuario. Por ejemplo la función rand() no estaría disponible.

Todas las funciones en coma flotante mencionadas arriba se describen en la sección Funciones en Coma Flotante.

6.1.6.1  Funciones de Suma y Producto

prod(i, b, n, a) La función producto.

Función Producto
Función producto

sum(i, b, n, a) La función Suma.

Función Suma
Función suma

Para ambos, prod y sum: i son cualquier nombre de variable y a es cualquier expresión, normalmente dependiente de i. Los valores b y n son también cualquier expresión.

Ejemplo:

  #declare factorial = function(C) { prod(i, 1, C, i) }
  #declare A = factorial(5);

El primer parámetro es el nombre de la variable de iteración. El segundo es la expresión del valor inicial y el tercero es la expresión del valor final. Éstos no pueden depender de la variable de iteración, pero la variable de iteración aun puede ser utilizada dentro de esas dos expresiones, aunque su valor es indefinido. La última expresión es la expresión a través de la cual se va a repetir. Se puede utilizar una variable para ese propósito.

El ámbito de una variable de iteración es la función que opera sobre la secuencia. Esto es, una variable de iteración se define sólo cuando se utiliza dentro de una función sum/prod. Por supuesto, las funciones sum/prod se pueden anidar. Sin emabrgo, hay un límite máximo de 56 variables locales definidas simultáneamente, lo que esencialmente significa que, en cualquier combinación de funciones sum/prod, no puedes anidar más de 56 niveles.

La variable de iteración se incrementa en uno a cada paso, pero su valor inicial y final puede ser cualquier valor. La iteración continuará hasta que el valor de iteración sea menor o igual al valor final.

Nota: Dado que el valor de iteración es una variable en coma flotante, el hecho de sumar uno añade ciertas alteraciones en las iteraciones largas, y de esta manera la precisión del coma flotante será un problema en algunos casos, y debe considerarse permitir un error razonable en el valor final.

Si la expresión a añadir tiene un signo negativo será, por supuesto, sustraida. De esa manera, cambiando el signo permitirá generar valores negativos en la función de suma. Igualmente multiplicando por 1/expresión efectivamente crea una división cuando se utiliza en la función producto.

Obviamente, para funcionar, el valor inicial del resultado es el elemento neutral de la operación. Esto es, un cálculo de suma empieza con 0 y un cálculo de producto empieza con 1 de la misma manera que se hace en las funciones de suma y producto en las matemáticas "ordinarias".

Se debe observar que, matemáticamente, tanto la suma como el producto son redundantes porque:

    log10(prod(i, b, n, a)) = sum(i, b, n, log10(a))
Lo que implica que una suma puede ser representada como un producto y viceversa, observando las reglas matemáticas usuales de los logaritmos, por supuesto. De todas maneras, como los logaritmos y sus inversos (potencias) son lentos de calcular, se proporcionan ambos.

6.1.6.2  Funciones y Macros

Puedes utilizar macros en las funciones, pero las macros se llamarán unicamente cuando la función se define, no cada vez que se llama a la función. No se pueden pasar las variables de función a las macros.

Puedes pasar funciones a las macros. La manera de hacerlo se explica mejor con un ejemplo:

  #macro Foo( Bar, X )
    #declare Y = Bar(X);
    #declare Z = Bar(Y);
  #end

  #declare FUNC=function(n){n+2}

  Foo(FUNC, 1)

  #debug str(Y,5,5)
  #debug "\n"
  #debug str(Z,5,5)
  #debug "\n"

6.1.6.3  Declarando funciones de coma flotante definidas por el usuario

Se puede declarar una función definida por el usuario utilizando las directivas #declare o #local. Por defecto una función toma tres parámetros y no tienes que especificar explícitamente sus nombres. Los tres parámetros por defecto son x, y y z. Por ejemplo:

 #declare foo = function { x + y * z }

Si necesitas más o menos parámetros tienes que especificar explícitamente una lista de parámetros.

Nota: x y u así como y y v son equivalentes y no se deben especificar ambos nombres de parámetros. Tampoco se deben especificar dos o más parámetros con el mismo nombre. Si se hiciese el resultado sería un error de interpretación o resultados de función indefinidos.

Lo siguiente son funciones válidas con parámetros:

 #declare foo2 = function(x, y, z) { x + y * z }
 #declare foo3 = function(k1, k2, z, y) { x + y * z + k1 * y + k2 }
 #declare foo4 = function(h) { h * h + h }
 #declare foo4 = function(u, v) { x + y * v } //=u + v*v
 #declare foo4 = function(x, v, z) { u + y * v + z } //=x + v*v + z

Límites:

Nota: No está permitido redeclarar funciones directamente. La manera de hacer esto es utilizando primero la sentencia undef para destruir la definicion previa.

Hay una función especial de tipo coma flotante. Puedes declarar una función de patrón:

Nota: La sintaxis es idéntica a la de los patrones, aun así no hay que especificar los colores. El resultado siempre es un valor en coma flotante, no un vector de color, como sería devuelto por una función que contuviera un pigmento.

 #declare foo = function {
   pattern {
     checker
   }
 }

Nota: El número de parámetros de los tipos de funciones especiales se determina automáticamente, así que no se tendrán que especificar los nombres de los parámentros.

6.1.6.4  Declarando Funciones de Vectores Definidas por el Usuario

Actualmente sólo se pueden declarar funciones de vectores utilizando uno de los tipos especiales de funciones. Los tipos soportados son las funciones de tranformaciones y de splines. Por ejemplo:

 #declare foo = function {
   transform {
     rotate <90, 0, 0>
     scale 4
   }
 }

 #declare mivector = foo(4, 3, 7);

 #declare foo2 = function {
   spline {
     linear_spline
     0.0, <0,0,0>
     0.5, <1,0,0>
     1.0, <0,0,0>
   }
 }

 #declare mivector2 = foo2(0.7);

Las funciones de splines tienen en cuenta el tamaño del vector. Esto es, una función que contiene un spline con cinco componentes también devolverá un vector de cinco componentes (uno por cada color), una función que contenga un spline con dos componentes solo devolverá un vector de dos componentes, y así sucesivamente.

Nota: el número de parámetros de los tipos de función especiales se determina automáticamente, así que no necesitas especificar los nombres de los parámetros

6.1.6.5  Declarando funciones de color definidas por el usuario

Actualmente sólo se pueden declarar funciones de colores utilizando uno de los tipos especiales de funciones. El único tipo soportado es la función pigment. Se puede utilizar cualquier pigmento válido. Éste es un ejemplo muy sencillo:

 #declare foo = function {
   pigment {
     color red 1
   }
 }

 #declare Vec = foo(1,2,3)

Un ejemplo utilizando un patrón:

 #declare foo = function {
   pigment {
     crackle
     color_map {
       [0.3, color Red]
       [1.0, color Blue]
     }
   }
 }

 #declare Val = foo(2,3,4).gray

Nota: el número de parámetros de los tipos de función especiales se determina automáticamente, así que no necesitas especificar los nombres de los parámetros

6.1.6.6  Funciones Predefinidas Internas

Algunas funciones ya están predefinidas. Se puede acceder a estas funciones internas a través de "functions.inc", que debe ser incluido primero en tu escena.

El número de parámetros requeridos, y lo que éstos controlan, también está definido en el archivo de inclusión, pero el capítulo "functions.inc" en la sección "Standard Include File" aporta más información.