Ilusión óptica en python

Estándar

En esta primera entrada tras la inaguración, mostraré cómo generar una animación para una increíble ilusión óptica. Se trata de la siguiente ilusión:

Se puede ver que el círculo de dentro parece estar girando en el exterior. Sin embargo esto no es del todo cierto. En realidad cada punto se mueve por una línea recta. La explicación completa está en esta página.

Para generarla, crearemos fotograma a fotograma la animación. Para ello usaremos la librería Pillow en Python.

En primer lugar, importaremos las librerías necesarias:

import PIL.Image as Image, PIL.ImageDraw as ImageDraw
from math import cos, sin, pi

A continuación, definiremos las constantes, tales como el tamaño de la imagen, el radio de los círculos, etc ...

N=100 #Número de fotogramas
P=8 #Número de puntos del círculo interior
H=600 #Altura de la imagen. En todo momento supondremos que la altura es menor o igual que la anchura
W=800 #Anchura de la imagen
R=20 #Diámetro de los círculos pequeños
C_circulo = 'blue' #Color del círculo externo
C_puntos = 'white' #Color de los puntos del círculo interno

Una vez hecho esto, crearemos una imagen base que contendrá el círculo externo. Cada fotograma será una copia de esta imagen con los puntos del círculo interno superpuestos.

im = Image.new('RGB', (W,H))
draw = ImageDraw.Draw(im)
margin = (W - H)/2
draw.ellipse((margin,0,H+margin,H),fill='blue') #Las coordenadas indican el cuadrado que contiene el círculo externo

A continuación, entraremos en un bucle, en el que se generará una imagen en cada iteración. Para cada imagen se crea una instancia de PIL.ImageDraw para dibujar los puntos del círculo interior.

for n in range(0,N):
    frame = im.copy()
    draw = ImageDraw.Draw(frame)

Ahora viene lo complicado. Volviendo a la explicación de la ilusión, podemos inferir la fórmula que rige el movimiento en el eje vertical. Al principio de la animación, supondremos que el punto está en lo más alto del círculo interior. En el ciclo completo, bajará hasta abajo y volverá luego a su punto inicial. Este movimiento será la proyección de la cicloide sobre el eje vertical:

Por tanto, para el punto que se mueve sobre el eje vertical, el centro tendrá las siguientes coordenadas:

donde es el número de fotograma. Con esta fórmula, el punto se moverá de arriba a abajo y viceversa en el transcurso de la animación, con la velocidad adecuada, ya que sigue la fórmula de la cicloide.

Suponiendo que tenemos un número par de puntos, se puede ver que en el momento inicial, el círculo opuesto debe estar en el centro del círculo mayor. Puesto que este punto (el cuarto de un total de 8) recorre la recta perpendicular a la primera, la trayectoria que realizará irá del centro a un extremo, vuelta al centro y continuación al otro extremo y vuelta al centro. Es decir, el mismo recorrido que el primer punto, solo que con un desfase de y proyectado sobre el eje horizontal (formando un ángulo de . Siguiendo un razonamiento parecido para cada uno de los puntos, podemos encontrar una fórmula que describe la trayectoria de cada punto:



donde es el número del punto, empezando en 0 para el que recorre la línea vertical hasta 7 para el último punto siguiendo las agujas del reloj.
Por tanto, dentro del bucle for anterior, incluimos uno nuevo que pintará cada uno de los puntos en la posición adecuada.

    for j in range(0,8):
        x = (W/2)+(H/2)*(cos(2*pi*n/N + pi*j/8))*sin(pi*j/8)
        y = (H/2)-(H/2)*(cos(2*pi*n/N + pi*j/8))*cos(pi*j/8)
        draw.ellipse((x-R, y-R, x+R, y+R), fill=C_puntos)

Finalmente, para cada fotograma guardamos la imagen en una carpeta de salida:

    frame.save('out/image' + str(n) + '.png')

Este script generará una serie de fotogramas en la carpeta de salida, que se pueden montar con un programa como ImageMagick en forma de GIF animado. El resultado se puede ver a continuación.

Resultado del algoritmo

Salida de nuestro script ya montado con ImageMagick

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *