Cutting Möbius Strip in Makie.jl

Table of Contents
We are tasked with visualizing the process of cutting a Möbius strip in half, and color the two sides with different colors using Makie.jl, a powerful plotting library in Julia.
We will use the following parameterization of the Möbius strip:
$$ \begin{align*}x&=\left(r+(2-v)\cos\frac{u}{2}\right)\cos\,u\\y&=\left(r+(2-v)\cos\frac{u}{2}\right)\sin\,u\\z&=(2-v)\sin\frac{u}{2}\end{align*} $$where $0\leq v \leq 2$, $0\leq u \leq 4\pi$ and $r$ controls the size. It has normal vector
$$ \begin{pmatrix} \frac12\left(r\sin\frac{u}{2}-r\sin\frac{3u}{2}-2(v-2)\sin\,u\sin^2\frac{u}{2}\right) \\ \frac12\left((v-2)(\sin^2 u+\cos\,u)-2r\sin\frac{u}{2} \sin\,u\right) \\ \left(r-(v-2)\cos\frac{u}{2}\right)\cos\frac{u}{2} \end{pmatrix}. $$To cut the strip, we limit $v \leq v_{cut}$, where $v_{cut}$ is a parameter that we will vary from $0$ to $2$. When $v_{cut} = 2$, we have the full strip.
Preliminaries #
The following code is tested in Julia 1.11.6 with Makie 0.24.6. We will be using the GLMakie
backend for fast and interactive 3D rendering (see Makie Docs for installation instructions).
Problem Setup #
using GLMakie
# Define the parametric equations for the Mobius strip
r = 5
x(u, v) = (r + (2 - v) * cos(u / 2)) * cos(u)
y(u, v) = (r + (2 - v) * cos(u / 2)) * sin(u)
z(u, v) = (2 - v) * sin(u / 2)
# normal vector
n(u, v) = begin
x = 1 / 2 * (r * sin(u / 2) - r * sin(3 / 2 * u) - 2 * (v - 2) * sin(u) * sin(u / 2)^2)
y = 1 / 2 * ((v - 2) * (sin(u)^2 + cos(u)) - 2r * sin(u / 2) * sin(u))
z = (r - (v - 2) * cos(u / 2)) * cos(u / 2)
norm = sqrt(x^2 + y^2 + z^2)
(x / norm, y / norm, z / norm)
end
Simple Surface Plot #
We will plot the full Möbius strip first with minimal styling.
fig = Figure(resolution=(800, 600))
ax = Axis3(fig[1, 1],
alignmode=Outside(-300),
aspect=(1, 1, 0.3),
viewmode=:fit,
protrusions=(0, 0, 0, 0)
)
hidedecorations!(ax)
hidespines!(ax)
# u goes from 0 to 4π
u = LinRange(0, 4pi, 300)
# v goes from 0 to 2
v = LinRange(0, 2, 20)
# Generate the surface points
X = x.(u, v')
Y = y.(u, v')
Z = z.(u, v')
# use u value for coloring
color = sin.(u) * ones(length(v))'
surface!(ax, X, Y, Z; color=color, shading=false, colormap=:reds)
fig
results in:
Cutting the Strip #
To cut the strip, modify the range of v
to go from 0
to v_cut
.
...
v_cut = 4/3
v = LinRange(0, v_cut, 20)
...
results in:
Coloring the Two Sides #
After cutting, the strip has two sides (orientable). We can color them differently based on whether the normal vector is pointing towards or away from the viewer. In fact, this is fairly simple to do in raw openGL with custom shaders, but GLMakie
does not support this level of customization. Instead, we will use a workaround by plotting two surfaces with slightly offset along the normal vector. Add the following code after the surface!
call above.
# shift vertices along normal
norms = n.(u, v')
c = 0.001
X1 = X .+ c * getindex.(norms, 1)
Y1 = Y .+ c * getindex.(norms, 2)
Z1 = Z .+ c * getindex.(norms, 3)
surface!(ax, X1, Y1, Z1; color=color, shading=false, colormap=:blues)
results in:
Animation #
Finally, we can animate by rotating the view.