Skip to content

Instantly share code, notes, and snippets.

@deeplook
Last active May 2, 2019 08:46
Show Gist options
  • Save deeplook/9c76c4fde7f13c52580ac824e86c5c5e to your computer and use it in GitHub Desktop.
Save deeplook/9c76c4fde7f13c52580ac824e86c5c5e to your computer and use it in GitHub Desktop.
This generates a Möbius strip using ipyvolume. (3D plots are not rendered inside this gist!)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Möbius Strip\n",
"\n",
"This generates a Möbius strip using ipyvolume.\n",
"\n",
"The data points are calculated using the parametric form described in \n",
"https://jakevdp.github.io/PythonDataScienceHandbook/04.12-three-dimensional-plotting.html#Example:-Visualizing-a-M%C3%B6bius-strip\n",
"\n",
"Still desired: add a texture like it is available with `ipv.examples.klein_bottle(texture=image)`. Any help welcome!!"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import ipyvolume as ipv"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def calculate_points(num_theta=30, num_width=4):\n",
" \"Calculate x, y, and z vectors for a Möbius strip.\"\n",
" theta = np.linspace(0, 2 * np.pi, num_theta)\n",
" width = np.linspace(-0.4, 0.4, num_width)\n",
" width, theta = np.meshgrid(width, theta)\n",
" phi = 0.5 * theta\n",
" r = 1 + width * np.cos(phi) # radius in x-y plane\n",
" x = np.ravel(r * np.cos(theta))\n",
" y = np.ravel(r * np.sin(theta))\n",
" z = np.ravel(width * np.sin(phi))\n",
" return x, y, z"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Points only"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "069e572ad2e64a1c8cea62096493b371",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Figure(camera=PerspectiveCamera(fov=46.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x, y, z = calculate_points()\n",
"ipv.figure()\n",
"ipv.scatter(x, y, z, size=4, color=np.array([x, y, z]).T, marker='sphere')\n",
"ipv.squarelim()\n",
"ipv.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calculate triangulation mesh manually"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e13da38bf7a048e0a1cb14d2dc408ec7",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Figure(camera=PerspectiveCamera(fov=46.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"nt, nw = 30, 4\n",
"x, y, z = calculate_points(num_theta=nt, num_width=nw)\n",
"# works only for num_w == 4\n",
"rng = range(0, nt*nw-nw, nw)\n",
"triangles = [[i, i+1, i+4] for i in rng]\n",
"triangles += [[i+1, i+4, i+5] for i in rng]\n",
"triangles += [[i+1, i+2, i+5] for i in rng]\n",
"triangles += [[i+2, i+5, i+6] for i in rng]\n",
"triangles += [[i+2, i+3, i+6] for i in rng]\n",
"triangles += [[i+2, i+3, i+6] for i in rng]\n",
"triangles += [[i+3, i+6, i+7] for i in rng]\n",
"\n",
"ipv.figure()\n",
"# ipv.scatter(x, y, z)\n",
"# ipv.plot(x, y, z)\n",
"ipv.plot_trisurf(x, y, z, triangles=triangles)\n",
"ipv.squarelim()\n",
"ipv.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calculate mesh automatically for any theta/width parameters"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def tri(num_theta, num_width):\n",
" \"Triangulate a Möbius stripe into a list of index point triples (i, j, k) for mesh.\"\n",
" nt, nw = num_theta, num_width\n",
" for i in range(0, nt*nw-nw, nw):\n",
" for j in range(nw-1):\n",
" yield [i+j, i+j+1, i+j+nw]\n",
" yield [i+j+1, i+j+nw, i+j+nw+1]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "64a99bf416474e22a75bf49c3fbd58fe",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Figure(camera=PerspectiveCamera(fov=46.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, …"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"nt, nw = 30, 8\n",
"x, y, z = calculate_points(num_theta=nt, num_width=nw)\n",
"ipv.figure()\n",
"ipv.scatter(x, y, z, marker='sphere')\n",
"# ipv.plot(x, y, z, color='orange')\n",
"triangles = list(tri(nt, nw))\n",
"ipv.plot_trisurf(x, y, z, triangles=triangles)\n",
"ipv.squarelim()\n",
"ipv.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment