Skip to content

Commit ea9fa85

Browse files
authored
Merge pull request #70 from gouarin/superellipsoid
add superellipsoid example
2 parents 9415747 + 1910b17 commit ea9fa85

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed

examples/superellipsoid.ipynb

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# How to use pythreejs to plot a superellipsoid"
8+
]
9+
},
10+
{
11+
"cell_type": "markdown",
12+
"metadata": {},
13+
"source": [
14+
"A superellipsoid is given by a parametric function and the equation is very similar to an ellipse equation. We only have different exponents which give us different shapes. For more informations: https://en.wikipedia.org/wiki/Superellipsoid.\n",
15+
"\n",
16+
"The idea of this example is to construct the mesh of the square $[0, 1]\\times[0,1]$ and to do a projection of these points on the superillipse which is the 2D shape and then to do a spherical product to have the 3D shape."
17+
]
18+
},
19+
{
20+
"cell_type": "code",
21+
"execution_count": 2,
22+
"metadata": {
23+
"collapsed": false
24+
},
25+
"outputs": [],
26+
"source": [
27+
"import numpy as np\n",
28+
"\n",
29+
"n = 10 # number of discretisation points for the square in each direction \n",
30+
"x_box = np.concatenate((np.linspace(-1, 1., n), np.ones(n-2), np.linspace(1, -1., n), -np.ones(n-2)))\n",
31+
"y_box = np.concatenate((-np.ones(n-1), np.linspace(-1, 1., n), np.ones(n-2), np.linspace(1, -1., n-1, endpoint=False)))\n",
32+
"nx_box = x_box.size\n",
33+
"\n",
34+
"coords = np.empty((nx_box**2, 3))\n",
35+
"\n",
36+
"def superellipse(rx, ry, m):\n",
37+
" \"\"\"\n",
38+
" superellipse formula with the projection of the unit square\n",
39+
" \n",
40+
" Parameters\n",
41+
" ----------\n",
42+
" rx : the radius in the x direction \n",
43+
" ry : the radius in the y direction \n",
44+
" m : the exponent of the superellipse\n",
45+
" \n",
46+
" Output\n",
47+
" ------\n",
48+
" the coordinates of the superellipse\n",
49+
" \"\"\"\n",
50+
" return x_box*rx*(1. - .5*np.abs(y_box)**(2./m))**(m/2.), y_box*ry*(1. - .5*np.abs(x_box)**(2./m))**(m/2.)\n"
51+
]
52+
},
53+
{
54+
"cell_type": "code",
55+
"execution_count": 3,
56+
"metadata": {
57+
"collapsed": true
58+
},
59+
"outputs": [],
60+
"source": [
61+
"def superellipsoid(rx, ry, rz, m1, m2):\n",
62+
" \"\"\"\n",
63+
" superellipsoid formula with the spherical product of two superellipse\n",
64+
" and update of the global coords array\n",
65+
" \n",
66+
" Parameters\n",
67+
" ----------\n",
68+
" rx : the radius in the x direction \n",
69+
" ry : the radius in the y direction \n",
70+
" rz : the radius in the z direction \n",
71+
" m1 : the exponent of the first superellipse\n",
72+
" m2 : the exponent of the second superellipse\n",
73+
" \"\"\" \n",
74+
" gx, gy = superellipse(1, 1, m2)\n",
75+
" hx, hy = superellipse(1, 1, m1)\n",
76+
"\n",
77+
" \n",
78+
" coords[:, 0] = rx*(gx[np.newaxis, :]*hx[:, np.newaxis]).flatten()\n",
79+
" coords[:, 1] = ry*(gx[np.newaxis, :]*hy[:, np.newaxis]).flatten()\n",
80+
" coords[:, 2] = rz*(gy[np.newaxis, :]*np.ones(hx.size)[:, np.newaxis]).flatten()"
81+
]
82+
},
83+
{
84+
"cell_type": "code",
85+
"execution_count": 4,
86+
"metadata": {
87+
"collapsed": false
88+
},
89+
"outputs": [],
90+
"source": [
91+
"# superellipsoid parameters\n",
92+
"rx = ry = rz = 1.\n",
93+
"m1 = m2 = 1.\n",
94+
"\n",
95+
"superellipsoid(rx, ry, rz, m1, m2)"
96+
]
97+
},
98+
{
99+
"cell_type": "markdown",
100+
"metadata": {},
101+
"source": [
102+
"We construct the triangulation by using the ConveHull function in scipy."
103+
]
104+
},
105+
{
106+
"cell_type": "code",
107+
"execution_count": 5,
108+
"metadata": {
109+
"collapsed": false
110+
},
111+
"outputs": [],
112+
"source": [
113+
"import scipy.spatial as spatial\n",
114+
"\n",
115+
"cvx = spatial.ConvexHull(coords)"
116+
]
117+
},
118+
{
119+
"cell_type": "code",
120+
"execution_count": 6,
121+
"metadata": {
122+
"collapsed": false
123+
},
124+
"outputs": [],
125+
"source": [
126+
"from pythreejs import *\n",
127+
"from IPython.display import display\n",
128+
"\n",
129+
"surf_g = PlainGeometry(vertices=coords.tolist(), faces=cvx.simplices.tolist())\n",
130+
"surf = Mesh(geometry=surf_g, material=BasicMaterial(color='green', wireframe=True))\n",
131+
"scene = Scene(children=[surf, AmbientLight(color='#777777')])\n",
132+
"c = PerspectiveCamera(position=[2, 2, 3], up=[0, 0, 1],\n",
133+
" children=[DirectionalLight(color='white',\n",
134+
" position=[3, 5, 1],\n",
135+
" intensity=0.6)])\n",
136+
"renderer = Renderer(camera=c, scene=scene, controls=[OrbitControls(controlling=c)])\n",
137+
"display(renderer)"
138+
]
139+
},
140+
{
141+
"cell_type": "code",
142+
"execution_count": 7,
143+
"metadata": {
144+
"collapsed": true
145+
},
146+
"outputs": [],
147+
"source": [
148+
"from ipywidgets import FloatSlider, HBox, VBox\n",
149+
"\n",
150+
"m1_slider, m2_slider = (FloatSlider(description='m1', min=0.01, max=4.0, step=0.01, value=m1,\n",
151+
" continuous_update=False, orientation='vertical'),\n",
152+
" FloatSlider(description='m2', min=0.01, max=4.0, step=0.01, value=m2,\n",
153+
" continuous_update=False, orientation='vertical'))"
154+
]
155+
},
156+
{
157+
"cell_type": "code",
158+
"execution_count": 8,
159+
"metadata": {
160+
"collapsed": true
161+
},
162+
"outputs": [],
163+
"source": [
164+
"rx_slider, ry_slider, rz_slider = (FloatSlider(description='rx', min=0.01, max=10.0, step=0.01, value=rx, \n",
165+
" continuous_update=False, orientation='horizontal'),\n",
166+
" FloatSlider(description='ry', min=0.01, max=10.0, step=0.01, value=ry, \n",
167+
" continuous_update=False, orientation='horizontal'),\n",
168+
" FloatSlider(description='rz', min=0.01, max=10.0, step=0.01, value=rz, \n",
169+
" continuous_update=False, orientation='horizontal'))"
170+
]
171+
},
172+
{
173+
"cell_type": "code",
174+
"execution_count": 9,
175+
"metadata": {
176+
"collapsed": false
177+
},
178+
"outputs": [],
179+
"source": [
180+
"def update(change):\n",
181+
" superellipsoid(rx_slider.value, ry_slider.value, rz_slider.value, \n",
182+
" m1_slider.value, m2_slider.value)\n",
183+
" surf_g.vertices = coords.tolist()\n",
184+
" \n",
185+
"m1_slider.observe(update, names=['value'])\n",
186+
"m2_slider.observe(update, names=['value'])\n",
187+
"rx_slider.observe(update, names=['value'])\n",
188+
"ry_slider.observe(update, names=['value'])\n",
189+
"rz_slider.observe(update, names=['value'])"
190+
]
191+
},
192+
{
193+
"cell_type": "code",
194+
"execution_count": 10,
195+
"metadata": {
196+
"collapsed": false
197+
},
198+
"outputs": [],
199+
"source": [
200+
"VBox([HBox([renderer, m1_slider, m2_slider]), rx_slider, ry_slider, rz_slider])"
201+
]
202+
},
203+
{
204+
"cell_type": "code",
205+
"execution_count": null,
206+
"metadata": {
207+
"collapsed": true
208+
},
209+
"outputs": [],
210+
"source": []
211+
}
212+
],
213+
"metadata": {
214+
"anaconda-cloud": {},
215+
"kernelspec": {
216+
"display_name": "Python [conda root]",
217+
"language": "python",
218+
"name": "conda-root-py"
219+
},
220+
"language_info": {
221+
"codemirror_mode": {
222+
"name": "ipython",
223+
"version": 3
224+
},
225+
"file_extension": ".py",
226+
"mimetype": "text/x-python",
227+
"name": "python",
228+
"nbconvert_exporter": "python",
229+
"pygments_lexer": "ipython3",
230+
"version": "3.5.2"
231+
},
232+
"widgets": {
233+
"state": {
234+
"5eb4554c95814fc6b83135c8b1a7a5ee": {
235+
"views": [
236+
{
237+
"cell_index": 8
238+
}
239+
]
240+
},
241+
"b9b3e029e6554c0caee4a3cd691eb821": {
242+
"views": [
243+
{
244+
"cell_index": 12
245+
}
246+
]
247+
}
248+
},
249+
"version": "1.2.0"
250+
}
251+
},
252+
"nbformat": 4,
253+
"nbformat_minor": 1
254+
}

0 commit comments

Comments
 (0)