Hopf torus with dynamic colors
In a recent post I explained how to decorate a surface with moving colors with the Python library PyVista. Here I expose this method for the R package rgl.
I will take a Hopf torus with fives lobes for the illustration. The following R function is a slight modification of the parametric3d
function of the misc3d package. It is more efficient for the Hopf torus parameterization and it returns a rgl object of class mesh3d
.
library(misc3d)
library(rgl)
function(
parametricMesh3d <-
Fxyz, umin, umax, vmin, vmax, nu, nv
){ seq(umin, umax, length.out = nu)
u <- seq(vmin, vmax, length.out = nv)
v <- misc3d:::expandTriangleGrid(u, v)
tg <- function(uv) Fxyz(uv[, 1L], uv[, 2L])
f <- f(tg$v1)
v1 <- f(tg$v2)
v2 <- f(tg$v3)
v3 <- makeTriangles(v1, v2, v3)
tris <- misc3d:::t2ve(tris)
mesh0 <-addNormals(
tmesh3d(
vertices = mesh0$vb,
indices = mesh0$ib
)
) }
The R function below is the parameterization of the Hopf torus.
function(u, v, nlobes = 5, A = 0.38){
HTxyz <- pi/2 - (pi/2-A)*cos(u*nlobes)
C <- sin(C)
sinC <- u + A*sin(2*u*nlobes)
D <- cos(C) + 1
p1 <- sinC * cos(D)
p2 <- sinC * sin(D)
p3 <- cos(v)
cos_v <- sin(v)
sin_v <- cos_v*p3 + sin_v*p2
x1 <- cos_v*p2 - sin_v*p3
x2 <- sin_v * p1
x3 <- sqrt(2*p1) - cos_v*p1
xden <-cbind(x1/xden, x2/xden, x3/xden)
}
Let’s make the Hopf torus mesh now.
parametricMesh3d(
mesh <-umin = 0, umax = 2*pi, vmin = -pi, vmax = pi, nu = 600, nv = 400
HTxyz, )
We will assign a color to each point on the surface, according to the distance from the point to the origin. We calculate these distances below, and we linearly map them to the interval \([0, 2\pi]\).
sqrt(apply(mesh$vb[-4L, ], 2L, crossprod))
d <- 2*pi * (d - min(d)) / diff(range(d)) d <-
Now we introduce a color palette function. The trekcolors package has nice color palettes. I’m taking the dominion
palette.
library(trekcolors)
colorRamp(
fpalette <-trek_pal("dominion"), bias = 0.6, interpolate = "spline"
)
This function fpalette
assign a color, given by its RGB values, to each number between \(0\) and \(1\). As in Python, we will calculate \(\sin(d -t)\) to move the colors, with \(t\) varying from \(0\) to \(2\pi\). The sine function takes its values in \([-1, 1]\) so we will map this interval to \([0,1]\) with the affine function \(x \mapsto \frac{x+1}{2}\) in order to apply the fpalette
function.
We will also rotate the Hopf torus around the \(z\)-axis. By the symmetry of the Hopf torus, it suffices to make the rotation with an angle varying from \(0\) to \(2\pi/5\).
seq(0, 2*pi, length.out = 73)[-1L]
t_ <- seq(0, 2*pi/5, length.out = 73)[-1L]
angle_ <-for(i in seq_along(t_)){
fpalette( (sin(d-t_[i])+1)/2 )
RGB <-"material"]] <-
mesh[[ list(color = rgb(RGB[, 1L], RGB[, 2L], RGB[, 3L], maxColorValue = 255))
rotate3d(mesh, angle_[i], 0, 0, 1)
rmesh <-open3d(windowRect = c(50, 50, 562, 562))
view3d(0, 0, zoom = 0.55)
spheres3d(0, 0, 0, radius = 11, color = "white", alpha = 0) # to fix the view
shade3d(rmesh)
rgl.snapshot(sprintf("pic%03d.png", i))
close3d()
}
This code generates a series of png
files pic001.png
, …, pic072.png
. Using ImageMagick or gifski, we obtain this gif
animation from these files:
Very well. But these are not my favorite colors. And I prefer the Hopf torus with three lobes. Below it is, decorated with the klingon
color palette of trekcolors; I prefer this one.