Created
April 12, 2021 14:56
-
-
Save kimlaberinto/f78def64acb453e4796c90c770c1228f to your computer and use it in GitHub Desktop.
Klein Bottle Math Politcal Compass meme animation (Julia)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Source for math political compass klein bottle meme by @KimPLab on Twitter | |
## Tweet: https://twitter.com/KimPLab/status/1381621398636949511 | |
## Inspired by the math political compass torus meme by @jessebett | |
## https://twitter.com/jessebett/status/1379162611414138885 | |
## @jessebett source code notes: | |
## upcycled from torus knot fibration visualization: | |
## http://www.jessebett.com/TorusKnotFibration/torusknot.html | |
## Note: | |
## Code to plot the klein bottle mesh below is modified from @jessebett's code | |
using GLMakie | |
using Images | |
using Colors | |
using Animations | |
compass = load("mathcompass_crop_rot90CCW.jpg") | |
# make_translucent(rgb) = RGBA(rgb, 0.8) | |
# compass = map(make_translucent, compass) | |
# Klein Bottle parametrization from Wikipedia page | |
# https://en.wikipedia.org/wiki/Klein_bottle | |
# Image describes it as the parametrization provided by Robert Israel | |
# https://en.wikipedia.org/wiki/Klein_bottle#/media/File:Klein_bottle_translucent.png | |
function klein_bottle(u, v) | |
x = (-2/15)*cos(u)*(3*cos(v) - 30*sin(u)+90*(cos(u)^4)*sin(u)-60*(cos(u)^6)*sin(u)+5*cos(u)*cos(v)*sin(u)) | |
y = (-1/15)*sin(u)*(3*cos(v)-3*(cos(u)^2)*cos(v) - 48*(cos(u)^4)*cos(v)+48*(cos(u)^6)*cos(v) - 60*sin(u) + 5*cos(u)*cos(v)*sin(u) - 5*(cos(u)^3)*cos(v)*sin(u) - 80*(cos(u)^5)*cos(v)*sin(u) + 80*(cos(u)^7)*cos(v)*sin(u)) | |
z = (2/15)*(3 + 5*cos(u)*sin(u))*sin(v) | |
return (x,y,z) | |
end | |
# Plotting function modified from @jessebett original source code | |
# Additions include: using Osbervables/Nodes for animation and adapting it to a klein bottle | |
# https://twitter.com/jessebett/status/1379162611414138885 | |
function plot_kleinbottle!(s, a, b, u_start, v_start) | |
u = @lift(range($u_start, $u_start + pi*$a, length=100)) | |
v = @lift(range($v_start, $v_start + -2*pi*$b, length=100)) | |
meshgrid = @lift(Iterators.product($u, $v)) | |
xyz = @lift [klein_bottle(u, v) for (u, v) in $meshgrid] | |
tx = @lift [x for (x,y,z) in $xyz] | |
ty = @lift [y for (x,y,z) in $xyz] | |
tz = @lift [z for (x,y,z) in $xyz] | |
surf = surface!(s,tx,ty,tz, backlight = 1f0, ambient = Vec3f0(0.5*0.55)) | |
surf.color = compass | |
# Note of code above: backlight = 1f0, ambient = Vec3f0(0.5*0.55) | |
# Suggestion is from @ffreyer on Julia Slack. | |
# Related to bug with `ambient` being applied twice | |
# Pull Request Fix: https://github.com/JuliaPlots/GLMakie.jl/pull/178 | |
# If it is fixed by the time you run this code, feel free to omit `ambient = Vec3f0(0.5*0.55)` | |
return s | |
end | |
function make_animation() | |
ts = range(-0.6, 5, length=1400) | |
anim_a = Animation(0, 0.025, | |
sineio(prewait=0.1, postwait=0.1), | |
0.3, 0.2, | |
sineio(prewait=0.1, postwait=0.1), | |
1.0, 1.0, | |
4.0, 1.0, | |
sineio(prewait=0.1, postwait=0.1), | |
4.5, 0.040, | |
sineio(prewait=0.1, postwait=0.1), | |
5.0, 0.025) | |
anim_b = Animation(0, 0.025, | |
sineio(prewait=0.1, postwait=0.1), | |
0.3, 1.0, | |
sineio(prewait=0.1, postwait=0.1), | |
1.0, 1.0, | |
4.0, 1.0, | |
sineio(prewait=0.1, postwait=0.1), | |
4.5, 0.040, | |
sineio(prewait=0.1, postwait=0.1), | |
5.0, 0.025) | |
anim_u_start = Animation(1.0, 0.0, | |
linear(prewait=0.05, postwait = 0.0), | |
4.0, 1.0*pi*(-2.9), | |
sineio(prewait=0.05, postwait=0.0), | |
4.5, 1.0*pi*(-2.0), | |
sineio(prewait=0.0, postwait=0.0), | |
5.0, 1.0*pi*(-2.0)) | |
anim_v_start = Animation(1.0, 0.0, | |
5.0, 0.0) | |
anim_theta = Animation( | |
-0.6, -0.75*pi, | |
sineio(prewait=0.0, postwait=0.0), | |
-0.2, -1/2*pi, | |
sineio(prewait=0.0, postwait=0.0), | |
0.0, 0+pi/2+pi*0.05, | |
linear(prewait=0.02, postwait=0.0), | |
4., 3*pi, | |
sineio(prewait=0.0, postwait=0.2), | |
5.0, 4*pi-0.75*pi) | |
anim_camera_h = Animation(0., 0.1, | |
sineio(prewait=0.4, postwait=0.1), | |
1.0, 0.4, | |
sineio(prewait=0.1, postwait=0.1), | |
2.0, 0.5, | |
4.0, 0.5, | |
sineio(prewait=0.2, postwait=0.2), | |
5.0, 0.1) | |
anim_image_shift = Animation(-1.0, 0.0, | |
4.0, 8.5, | |
5.0, 8.5) | |
scene = Scene(resolution=(1000, 1000), scale=(1,1,1), show_axis=false) | |
aNode = Node(0.0) | |
bNode = Node(0.0) | |
u_startNode = Node(0.0) | |
v_startNode = Node(0.0) | |
plot_kleinbottle!(scene, aNode, bNode, u_startNode, v_startNode) | |
cam3d!(scene) | |
for (i, t) in enumerate(ts) | |
a = at(anim_a, t) #anim_a(t) should also work | |
b = at(anim_b, t) | |
u_start = at(anim_u_start, t) | |
v_start = at(anim_v_start, t) | |
theta = at(anim_theta, t) | |
camera_h = at(anim_camera_h, t) | |
aNode[] = a | |
bNode[] = b | |
u_startNode[] = u_start | |
v_startNode[] = v_start | |
println(" $i / $(length(ts)) = $(round(i/length(ts); sigdigits=4)) \t t = $(round(t; sigdigits=3)) \t theta = $(round(theta; sigdigits=4))") | |
update_cam!(scene, [cos(theta), sin(theta), camera_h], [0., 0., 0.]) | |
update!(scene) | |
filename = joinpath("anim/", lpad(i,5,"0") * ".png") | |
save(filename, scene) | |
end | |
end | |
make_animation() | |
# Uses ffmpeg to create animation (40 fps) | |
# (ffmpeg snippet I had lying around. Not sure how it works exactly... but gets the job done) | |
run(`ffmpeg -r:v 40 -i "anim/%05d.png" -codec:v libx264 -preset veryslow -pix_fmt yuv420p -crf 28 -an "anim.mp4" -y`) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment