He estado experimentando a ver si podía lograr algo parecido en Julia, y de momento tengo esto:
Spoiler
El código utilizado:
using GLMakie, Colors
# Parametrización de la cinta de Möbius
function f(t, θ)
((t * sin(θ / 2) + 1) * cos(θ), (t * sin(θ / 2) + 1) * sin(θ), t * cos(θ / 2))
end
# Rangos de los parámetros que van a utilizarse
r1 = range(-1 / 2, 1 / 2, length = 50) # el ancho de la cinta
r2 = range(0, 2pi, length = 100) # rango total del ángulo para dibujar la cinta completa
r3 = range(0, pi / 5, length = 20) # rango parcial del ángulo para dibujar un trozo de cinta
# Colores para la animación. Generamos dos matrices con colores que decaen, en dirección del ángulo
# hacia la transparencia. Cada matriz tiene un color base diferente y coloreará una de las caras
# de un trozo de cinta móvil que será lo que animemos
cara1 = [HSLA(350 + i * 3, 1, 0.6, (1 - i / 20)^2) for i = 0:19]
cara1 = repeat(cara1', 50)
cara2 = [HSLA(290 + i * 3, 0.9, 0.5, (1 - i / 20)^2) for i = 0:19]
cara2 = repeat(cara2', 50)
# Matriz de coordenadas de la cinta de Möbius completa, ahí x0, y0 y z0 son vectores de puntos
# de las coordenadas de la cinta
x0, y0, z0 = [[pt[i] for pt in f.(r1, r2')] for i = 1:3]
# Creamos un observable y las coordenadas de dos trozos de cinta de Möbius, cada trozo representa una
# de las caras del trozo de cinta móvil. El macro @lift encadena las coordenadas al observable t
# (que representa el tiempo), eso significa que cada vez que cambia el valor de t se va a grabar
# una imagen con el nuevo valor de t y todas las funciones que dependan de él
t = Node(0.0)
x1, y1, z1 = [@lift([pt[i] for pt in f.(r1, r3' .- $t)]) for i = 1:3]
x2, y2, z2 = [@lift([pt[i] for pt in f.(r1, r3' .- (2pi + $t))]) for i = 1:3]
# Creamos el lienzo donde dibujaremos
set_theme!(backgroundcolor = :black)
fig = Figure(resolution = (500, 400))
ax = Axis3(fig[1, 1], aspect = :data, elevation = 0.7, azimuth = 3)
hidedecorations!(ax)
hidespines!(ax)
# Dibujamos tres superficies, la primera es una cinta de Möbius completa y estática,
# y las otras dos superficies representan las dos caras de un trozo de cinta animado
surface!(ax, x0, y0, z0, colormap = (:Blues_3, 0.7), shading = false, transparency = true)
surface!(ax, x1, y1, z1, color = cara1)
surface!(ax, x2, y2, z2, color = cara2)
# Ahora animamos y guardamos en formato GIF. También se puede guardar en formato MP4 o MKV
framerate = 25 # los frames por segundo
timestamps = range(0, 4pi, step = 1 / framerate) # el número total de frames
record(fig, "./Julia/mobius_animation.gif", timestamps; framerate = framerate) do i
t[] = i
end
ahora los colores permiten diferenciar las caras de la parte móvil.