Más sobre fractales

En algún lugar de la literatura se menciona sobre recursividad y fractales. En esta ocasión solo presentaré algunos con los que estuve jugando hace algún tiempo y que he venido arrastrando para porfin ser publicados aquí. Nada sorprendente, solo ejercicios de la curiosidad.

Helecho de Barnsley

Versión de tres direcciones

El helecho de Barnsley para tres posibles direcciones
from turtle import *
from random import choice
from time import sleep

elecciones = [0, 1, 2]

if __name__ == "__main__":
    reset()
    speed(0)

    for i in range(20000):
        op = choice(elecciones)

        if op == 0:
            left(60)
            forward(2)
        elif op == 1:
            right(60)
            forward(4)
        else:
            backward(6)

    exitonclick()

Versión de dos direcciones

El helecho de Barnsley para dos posibles direcciones
from turtle import *
from random import choice

elec = [0, 1]
step = 5

if __name__ == "__main__":
    reset()
    speed(0)
    left(90)

    for i in range(800):
        if choice(elec):
            left(90)
            forward(step*3)
            dot(5, "blue")
        else:
            backward(step)
            dot(5, "blue")

    exitonclick()

Triángulo de Sierpinski

Esta versión se comporta un poco como la búsqueda en amplitud, pues primero dibuja el triángulo actual y después hace la recursión, dando la impresión de que va de lo grande a lo pequeño.

El algoritmo es bien sencillo:

  1. Dibuja un triángulo. 2. A ese triángulo le dibujas uno enmedio uniendo los puntos medios de los lados. 3. Se repite el proceso para los cuatro triángulos de las orillas.
El fractal de sierpinski
from turtle import *
from time import sleep

MAX_DEPTH = 7 # Máxima profundidad recursiva

def triangulo(lenght):
    """
    Hace un triangulo equilatero con una longitud dada
    desde donde este parada la tortuga
    """
    for i in range(3):
        forward(lenght)
        left(120)

def sierpinski(depth=0):
    avance = 300/float(2**depth)

    if depth < MAX_DEPTH:
        triangulo(avance)
        forward(avance/2)
        left(60)
        triangulo(avance/2)
        right(60)
        backward(avance/2)
        # comenzamos a darle profundidad recursiva
        sierpinski(depth+1)
        forward(avance/2)
        sierpinski(depth+1)
        left(60)
        forward(avance/2)
        left(60)
        sierpinski(depth+1)
        right(60)
        backward(avance/2)
        right(60)
        backward(avance/2)

if __name__ == "__main__":
    reset()
    shape('turtle')
    speed(0)
    backward(200)
    sierpinski()
    sleep(5)

Otra versión de Sierpinski

En este caso se aplica la recursión primero, análogo a como sería en una búsqueda en profundidad, dando el efecto al dibujar de que primero se construyen los triángulos más pequeños.

from turtle import *

MAX_DEPTH = 4

def thingy_left(length, depth=0):
    if depth > MAX_DEPTH:
        return forward(length)

    thingy_left(length/3, depth+1)
    left(60)
    thingy_right(length/3, depth+1)
    left(60)
    thingy_left(length/3, depth+1)
    right(60)
    thingy_right(length/3, depth+1)
    right(60)
    thingy_left(length/3, depth+1)
    right(60)
    thingy_right(length/3, depth+1)
    right(60)
    thingy_left(length/3, depth+1)
    left(60)
    thingy_right(length/3, depth+1)
    left(60)
    thingy_left(length/3, depth+1)

def thingy_right(length, depth=0):
    if depth > MAX_DEPTH:
        return forward(length)

    thingy_right(length/3, depth+1)
    right(60)
    thingy_left(length/3, depth+1)
    right(60)
    thingy_right(length/3, depth+1)
    left(60)
    thingy_left(length/3, depth+1)
    left(60)
    thingy_right(length/3, depth+1)
    left(60)
    thingy_left(length/3, depth+1)
    left(60)
    thingy_right(length/3, depth+1)
    right(60)
    thingy_left(length/3, depth+1)
    right(60)
    thingy_right(length/3, depth+1)

if __name__ == '__main__':
    home()
    speed(0)
    thingy_left(100)
    exitonclick()

Trébol

un bonito y simple trébol recursivo que además cambia de color con la profundidad.

Un trébol recursivo fractal
from turtle import *

COLOR = int('28', 16), int('50', 16), int('78', 16)
PASO_INICIAL = 100
MAX_DEPTH = 10

def hexa(num):
    """
    Convierte num a hexadecimal y lo devuelve
    como numero de dos digitos
    """
    r = str(hex(num))[2:]
    if int(r, 16) < 16:
        r = '0'+r
    if int(r, 16) < 256:
        return r
    else:
        return 'ff'

def trebol(depth=0):
    """
    Dibuja una rama de trebol
    """
    if depth >= MAX_DEPTH:
        return

    color('#'+hexa(COLOR[0]+15*depth)+hexa(COLOR[1]+15*depth)+hexa(COLOR[2]+15*depth))
    paso = PASO_INICIAL/float((8/5.)**depth)
    down()
    forward(paso)
    left(60)
    trebol(depth+1)
    right(120)
    trebol(depth+1)
    left(60)
    up()
    backward(paso)

if __name__ == "__main__":
    reset()
    shape('turtle')
    width(2)
    speed(0)
    color('#285078')
    trebol()
    left(120)
    trebol()
    left(120)
    trebol()
    exitonclick()

Un simple espiral

Sin nada de trucos raros, simplemente un bucle de dos líneas y ámonos.

Un sencillo espiral
from turtle import *
from time import sleep

if __name__ == "__main__":
    for i in range(90, 0, -1):
        forward(90-i)
        left(i)
    sleep(5)

Curva Z

Esta curva tiene muchas propiedades interesantes, como que puede pasar por todos (los infinitos) puntos de un área. De hecho me topé con ella tratando de entender un algoritmo para codificar coordenadas de un espacio bidimensional en un espacio unidimensional, técnica útil para hacer algunas operaciones geoespaciales.

Una curva-z que puede llenar un área
from turtle import *
from math import sqrt

SIDE_PIECES = 32
MAX_SIZE = 800
OFFSET_X = -MAX_SIZE/2
OFFSET_Y = -MAX_SIZE/2


def deinterleave(num, size):
    x = 0
    y = 0

    for i in range(size):
        x += (num % 2) << i
        num >>= 1

        y += (num % 2) << i
        num >>= 1

    return x, y


if __name__ == '__main__':
    speed('fastest')

    for i in range(0, SIDE_PIECES**2):
        x, y = deinterleave(i, SIDE_PIECES)

        if i == 0:
            up()

        goto(
            x * MAX_SIZE / SIDE_PIECES + (MAX_SIZE/(SIDE_PIECES*2)) + OFFSET_X,
            y * MAX_SIZE / SIDE_PIECES + (MAX_SIZE/(SIDE_PIECES*2)) + OFFSET_Y,
        )

        if i == 0:
            down()

    exitonclick()