Foto-blog de Antonio Castro

Retoque fotográfico con Gimp, fotos copyleyft, licencia Creative Commons (CC-BY), fotos gratis para uso comercial con reconocimiento.

Macrofotografía abeja

Etiqueta: Recursividad

Curioso fractal en 3D (Povray)

La he llamado «bola hexaédrica recursiva», porque no sabría qué otro nombre dar a esto tan raro.

Es una forma inesperada surgida de unas pruebas que hice. Me ha sorprendido el resultado. Cada bola da origen a otras seis bolas más pequeñas. De ahí el nombre, pero he intentado hacer algo similar usando un patrón tetraédrico y el resultado no es ni de lejos tan llamativo como este. Supongo que la gente que diseña fractales hace pruebas hasta que obtiene algún resultado curioso. Este sin duda lo es.

El vídeo está pensado para que se comprenda como surge la forma final. Sólo dura 1min y 16 segundos.

La macro recursiva que genera la forma es bastante sencilla y se ha implementado en un fichero include.

// hexarec.inc
// (C) Antonio Castro Snurmacher
// Licencia GPL de GNU
#declare NullObj=object{ sphere{<0,0,0>,0.00000000001} pigment {color rgb <0,0,0>} }
#macro Hexa(K, Level, T1, T2)
union{
object{
sphere {<0,0,0>, 1}
texture {T1}
}
#if (Level>0)
object{ Hexa(K, Level-1, T2, T1) scale K translate x }
object{ Hexa(K, Level-1, T2, T1) scale K translate -x }
object{ Hexa(K, Level-1, T2, T1) scale K translate y }
object{ Hexa(K, Level-1, T2, T1) scale K translate -y }
object{ Hexa(K, Level-1, T2, T1) scale K translate z }
object{ Hexa(K, Level-1, T2, T1) scale K translate -z }
#else
object {NullObj}
#end
}
#end

El programa principal que llama a esa macro recursiva, se complica algo con el fin de introducir algunas optimizaciones, sentencias para la animación y algunas mejoras estéticas. Es este:

// demo_hexarec.pov
// (C) Antonio Castro Snurmacher
// Licencia GPL de GNU
#include "hexarec.inc"
#include "colors.inc"
#include "textures.inc"
#include "shapes.inc"
#declare Oliva = pigment {color rgb <0, 0.5, 0.3>} // Verde oliva oscuro
#declare Azul = pigment {color rgb <.2, 0.6, 0.9>}
#declare AmarilloClaro = pigment {color rgb <1,1, 0.6>}
#declare RojoOscuro = pigment {color rgb <.7, 0, 0.1>} // Rojo oscuro
#declare Blanco = pigment {color rgb <.9,.9,.95>} // Blanco azulado
#declare Textu1=texture{
pigment {RojoOscuro}
finish{
reflection .4
ambient 0.2
diffuse 0.4
phong .4
phong_size 40
}
}
#declare Textu2=texture{
pigment {Blanco}
finish{
reflection 0
ambient 0.3
diffuse 0.7
phong .5
phong_size 20
}
}
#declare Textu3=texture{
pigment {AmarilloClaro}
finish{
reflection 0
ambient 0.3
diffuse 0.7
phong .5
phong_size 20
}
}
#declare Textu4=texture{
pigment {Azul}
finish{
reflection .6
ambient 0.1
diffuse 0.3
}
}
#declare Luz=light_source { <30, 40, -60> color White }
#declare CameraUp= camera {
location <0, 10, -40>
look_at <0,0,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
}
/**
Pensado para clock 1..1900
pov.py demo_hexarec t 11 11 1 1900 1 1900
**/
// En el intervalo de clock de 0 a 1000 va aumentando la recursividad luego ya no.
#declare Clock1 = clock/2000;
#declare Clock2=clock/2;
global_settings { charset utf8 }
#declare FontSize = 3;
#declare FonTTF = "aa_cooperbt.ttf"
#declare texto="CIBERDROIDE.COM"
#declare Clock3 = clock -1000;
#if (Clock3<0) #declare Clock3=0; #end #if (Clock3>0)
#declare PosText=19-(Clock3/12);
text { ttf FonTTF texto 1, 0
texture { Textu4 }
scale FontSize
translate // clock*??
}
text { ttf FonTTF texto 1, 0
texture { Textu3 }
scale
translate // clock*??
}
#end
/** Ajustar la recursividad al nivel de detalle requerido para
acelerar los primeros fotogramas. **/
#declare K=7;
#if (Clock1>.5)
#declare Clock1=.5;
#end
#if (Clock1<.4) #declare K=6; #end #if (Clock1<.3) #declare K=5; #end #if (Clock1<.2) #declare K=4; #end #if (Clock1<.1) #declare K=3; #end object{ Hexa(Clock1, K, Textu1, Textu2) /** LLAMADA A LA MACRO RECURSIVA **/ rotate y*Clock2 scale 8 } camera {CameraUp} Luz

El fichero ttf con la fuente "aa_cooperbt.ttf" no se proporciona, pero se puede usar cualquier fuente ttf.

Es la primera vez que usamos en la primitiva text. Como puedes ver en el fuente, su uso es trivial, y no hay ninguna otra cuestión técnica que hayamos mencionado anteriormente. En cualquier caso si tienes dudas no tienes más que preguntar aquí en los comentarios.

Estructuras recursivas en Povray (Tutorial)

Primera aproximación de un árbol de navidad.

Primera aproximación de un árbol de navidad.

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.

Rama de pino sin puas.

Rama de pino sin puas.

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.

Rama de pino con púas.

Rama de pino con púas.

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.

Funciona con WordPress & Tema de Anders Norén