Last active
May 2, 2019 08:46
-
-
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!)
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
{ | |
"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