Si nos fijamos en la imagen de cabecera, comprobamos una cantidad importante de elementos que se repiten. Los procesos recursivos son aquellos que se basan en su propia definición.
Cuando un problema complejo puede ser abordado resolviendo el mismo tipo de problema con una instancia del mismo más simple del mismo, el enfoque recursivo resultará idóneo.
Por ejemplo: podemos definir el factorial de un número como el producto de ese número multiplicado por el factorial de ese número menos uno, salvo en el caso de cero cuyo factorial vale uno. Expresemos esto en forma de seudocódigo.
factorial (x)
Si (x es igual a 0) –> 1
En caso contrario –> x*factorial(x-1)
Es evidente que las funciones recursivas deben tener una sentencia de salida de la recursividad. De otra forma no terminarían nunca de ejecutarse. Nosotros hemos usado un contador para limitar la profundidad de la recursividad al valor deseado.
En nuestro pino, en la imagen de cabecera, podemos ver la disposición de las ramas del pino en varios niveles. Para ello se usó un simple bucle (se podría haber optado por un enfoque recursivo). La razón es que debido al diferente peso de las ramas, habría que introducir complejos cálculos para respetar una estética más realista.
De todas formas si alguien cree que no es así, no descarto que sea posible encontrar una buena solución recursiva. Sería cuestión de intentarlo.
Por el contrario, para las ramas sí hemos comprobado que la solución recursiva resultaba simple y muy adecuada.
El fuente del árbol no tiene gran cosa de particular una vez que sabemos crear sus ramas que es lo que explicaremos en esta entrega. La potencia de la recursividad para construir formas complejas es algo que siempre resulta fascinante. Seguramente esta no será la única vez que recurramos a ella.
En Povray disponemos de macros que pueden llamarse a sí mismas para implementar una macro recursiva.
Esta es una primera aproximación de una rama sin púas. Mostramos la rama sin púas para que se aprecie mejor la estructura recursiva.
En la imagen hemos usado distintos colores para dos tipos de ramas implementadas con macros recursivas.
En la parte superior de la imagen, vemos una rama sin ramificaciones (es un simple tallo de color blanquecino) #macro rama(…). La segunda rama más abajo en verde y marrón es una rama ramificada (valga la redundancia) #macro RamaR(…)
// arbol_navidad.inc
// (C) Antonio Castro Snurmacher
// Licencia GPL de GNU
#include «colors.inc»
#include «textures.inc»
#include «shapes.inc»
#include «stones.inc»
#include «metals.inc»
#include «transforms.inc»
#declare TextuVerde=texture{
pigment {color rgb <0, 0.8, 0.4>}
finish{
ambient 0.2
diffuse 0.7
phong .4
phong_size 20
}
}
#declare TextuMarron=texture{
pigment {color rgb <.4, .35, 0>}
finish{
ambient 0.2
diffuse 0.7
}
}
#declare NullObj=sphere{<0,0,0>,0.00000000001}
#macro SegmentoPuasNull(Punto1, Rad1, Punto2,Rad2)
cone{Punto1, Rad1, Punto2, Rad2}
#end
#macro SegmentoPuas(Punto1, Rad1, Punto2,Rad2)
#local V=Punto2-Punto1;
#local LCone=vlength(V);
#local LPuas=LCone*.7;
#local RadPuas=LPuas/15;
#local NumAnillos=3;
#local NumPuasAnillo=10;
object {
union{
cone{<0,0,0>,Rad1, <0,LCone,0>,Rad2}
#local ContCirc=0;
#local ContAnillos=0;
#while (ContAnillos,<0,0,LPuas>,RadPuas
rotate y*Rot
translate <0,PosY, 0>
}
#local ContPuasAnillo=ContPuasAnillo+1;
#end
#local ContAnillos=ContAnillos+1;
#end
}
Reorient_Trans(y,V)
translate Punto1
}
// cone{Punto1, Rad1, Punto2, Rad2
#end
// Rama lineal.
#macro Rama(Punto1, Rad1, Punto2, Rad2, Iter, Textu, Reducc, Rot, SinPuas)
union{
#if (SinPuas)
SegmentoPuasNull(Punto1, Rad1, Punto2, Rad2)
#else
SegmentoPuas(Punto1, Rad1, Punto2, Rad2)
#end
#if (Iter>=0)
object{ Rama(Punto2, Rad1*Reducc, Punto2+ Reducc*(Punto2-Punto1), Rad2*Reducc, Iter-1, Textu, Reducc, Rot, SinPuas)
translate -Punto2
rotate -z*Rot
translate Punto2
}
texture {Textu}
#else object{NullObj} // Para suprimire el warning por hacer union con un solo elemento.
#end
}
#end
// Rama ramificada.
#macro RamaR(Punto1, Rad1, Punto2, Rad2, Iter, Textu1, Textu2, Reducc, Rot, Rot2, SinPuas)
union{
object{
#if (SinPuas)
SegmentoPuasNull(Punto1, Rad1, Punto2, Rad2)
#else
SegmentoPuas(Punto1, Rad1, Punto2, Rad2)
#end
texture {Textu1}
}
#if (Iter>=0)
object {RamaR(Punto2, Rad1*Reducc, Punto2+ Reducc*(Punto2-Punto1), Rad2*Reducc, Iter-1, Textu1, Textu2 Reducc, Rot, Rot2, SinPuas)
translate -Punto2
rotate -z*Rot
translate Punto2
}
object {Rama(Punto2, Rad1*Reducc, Punto2+ Reducc*(Punto2-Punto1), Rad2*Reducc, Iter-1, Textu2, Reducc, Rot, SinPuas)
translate -Punto2
rotate -z*Rot
rotate y*-Rot2
translate Punto2
texture {Textu2}
}
object {Rama(Punto2, Rad1*Reducc, Punto2+ Reducc*(Punto2-Punto1), Rad2*Reducc, Iter-1, Textu2, Reducc, Rot, SinPuas)
translate -Punto2
rotate -z*Rot
rotate y*Rot2
translate Punto2
texture {Textu2}
}
#else object{NullObj} // Para suprimire el warning por hacer union con un solo elemento.
#end
}
#end
Para usar este «include» hemos realizado un par de demos donde podemos apreciar su funcionamiento, para construir una rama.
// demo_rama_pino.pov
// (C) Antonio Castro Snurmacher
// Licencia GPL de GNU
#include «rama_pino.inc»
#declare Luz=light_source { <30, 60, -40> color Gray50 }
#declare CameraUp= camera {
location <8, 30, -40>
look_at <8,15,0>
angle 65
// AspectRatio debe declararse en el ‘.ini’
// Su valor es el ancho/alto y normalmente valdrá 16/9 o 4/3
right AspectRatio*x
}
#declare EscalaSuelo=8;
#declare Suelo2= plane { y,0
texture {T_Stone21}
scale EscalaSuelo
}
union{ Rama(<-6,30,0>, 1.22, <0,30,0>, 1.2, 15, pigment{White}, .85, 5, 1 )
pigment{White}
}
union{ RamaR(<-6,25,0>, 1.22, <0,25,0>, 1.2, 15, TextuMarron, TextuVerde, .85, 5, 60, 1 )}
camera {CameraUp}
Luz
Suelo2
La versión con púas es casi idéntica, solo cambia un parámetro al invocar la macro desde un par de líneas que cambian ligeramente
union{ Rama(<-6,30,0>, 1.22, <0,30,0>, 1.2, 15, pigment{White}, .85, 5, 0 )
pigment{White}
}
union{ RamaR(<-6,25,0>, 1.22, <0,25,0>, 1.2, 15, TextuMarron, TextuVerde, .85, 5, 60, 0 )}
Estamos usando los valores 1 (distinto de cero) y 0 como valores lógicos (cierto y falso). El resultado de la rama con púas queda según puede verse en la imagen siguiente.
Hemos usado simples cilindros como púas, ya que estas no están pensadas para ser vistas de cerca y dan el pego perfectamente. Una forma de púa más compleja repercutiría en la velocidad de obtención de la imagen, porque suman muchas púas y el programa debe calcularlas todas.
En esta última imagen ya empieza a aproximarse más a una verdadera rama de pino, pero no vamos a mostrar ahora como quedaría el árbol con estas ramas. Lo dejamos para la siguiente entrega, que será la última de esta serie navideña.