Skip to content

Commit 3c896f6

Browse files
xavArtleymaartenbreddels
xavArtley
authored andcommitted
Greatly improve mesh grid generation : more than 100x, look at mesh speed comparison notebook
1 parent 1ee095d commit 3c896f6

File tree

3 files changed

+293
-29
lines changed

3 files changed

+293
-29
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ ENV/
9595
# Rope project settings
9696
.ropeproject
9797

98+
# Eclipse stuff
99+
.settings
100+
.project
101+
102+
# Pydev stuff
103+
.pydevproject
104+
98105
# Webpack watch files
99106
js/node_modules/
100107
# Javascript Build files

ipyvolume/pylab.py

+86-29
Original file line numberDiff line numberDiff line change
@@ -321,35 +321,7 @@ def reshape_color(ar):
321321
if v is not None:
322322
v = reshape(v)
323323
_grow_limits(np.array(x).reshape(-1), np.array(y).reshape(-1), np.array(z).reshape(-1))
324-
mx = nx if wrapx else nx - 1
325-
my = ny if wrapy else ny - 1
326-
triangles = np.zeros(((mx) * (my) * 2, 3), dtype=np.uint32)
327-
lines = np.zeros(((mx) * (my) * 4, 2), dtype=np.uint32)
328-
329-
def index_from2d(i, j):
330-
xi = (i % nx)
331-
yi = (j % ny)
332-
return ny * xi + yi
333-
"""
334-
^ ydir
335-
|
336-
2 3
337-
0 1 ---> x dir
338-
"""
339-
340-
for i in range(mx):
341-
for j in range(my):
342-
p0 = index_from2d(i, j)
343-
p1 = index_from2d(i + 1, j)
344-
p2 = index_from2d(i, j + 1)
345-
p3 = index_from2d(i + 1, j + 1)
346-
triangle_index = (i * my) + j
347-
triangles[triangle_index * 2 + 0, :] = [p0, p1, p3]
348-
triangles[triangle_index * 2 + 1, :] = [p0, p3, p2]
349-
lines[triangle_index * 4 + 0, :] = [p0, p1]
350-
lines[triangle_index * 4 + 1, :] = [p0, p2]
351-
lines[triangle_index * 4 + 2, :] = [p2, p3]
352-
lines[triangle_index * 4 + 3, :] = [p1, p3]
324+
triangles, lines = _make_triangles_lines(x.reshape(nx,ny),y.reshape(nx,ny),z.reshape(nx,ny),wrapx,wrapy)
353325
# print(i, j, p0, p1, p2, p3)
354326
mesh = ipv.Mesh(x=x, y=y, z=z, triangles=triangles if surface else None, color=color,
355327
lines=lines if wireframe else None,
@@ -1004,3 +976,88 @@ def lasso(data, other=None, fig=fig):
1004976
if selected:
1005977
scatter.selected = selected
1006978
fig.on_lasso(lasso)
979+
980+
981+
982+
def _make_triangles_lines(x, y, z, wrapx=False, wrapy=False):
983+
"""Transform rectangular regular grid into triangles
984+
985+
:param x: {x2d}
986+
:param y: {y2d}
987+
:param z: {z2d}
988+
:param bool wrapx: when True, the x direction is assumed to wrap, and polygons are drawn between the end end begin points
989+
:param bool wrapy: simular for the y coordinate
990+
:return: triangles and lines used to plot Mesh
991+
"""
992+
assert len(x.shape) == 2, "Array x must be 2 dimensional."
993+
assert len(y.shape) == 2, "Array y must be 2 dimensional."
994+
assert len(z.shape) == 2, "Array z must be 2 dimensional."
995+
assert x.shape == y.shape, "Arrays x and y must have same shape."
996+
assert y.shape == z.shape, "Arrays y and z must have same shape."
997+
998+
nx, ny = x.shape
999+
1000+
mx = nx if wrapx else nx - 1
1001+
my = ny if wrapy else ny - 1
1002+
1003+
"""
1004+
create all pair of indices (i,j) of the rectangular grid
1005+
minus last row if wrapx = False => mx
1006+
minus last column if wrapy = False => my
1007+
| (0,0) ... (0,j) ... (0,my-1) |
1008+
| . . . . . |
1009+
| (i,0) ... (i,j) ... (i,my-1) |
1010+
| . . . . . |
1011+
|(mx-1,0) ... (mx-1,j) ... (mx-1,my-1) |
1012+
"""
1013+
i, j = np.mgrid[0:mx, 0:my]
1014+
1015+
"""
1016+
collapsed i and j in one dimensional array, row-major order
1017+
ex :
1018+
array([[0, 1, 2], => array([0, 1, 2, 3, *4*, 5])
1019+
[3, *4*, 5]])
1020+
if we want vertex 4 at (i=1,j=1) we must transform it in i*ny+j = 4
1021+
"""
1022+
i, j = np.ravel(i), np.ravel(j)
1023+
1024+
"""
1025+
Let's go for the triangles :
1026+
(i,j) - (i,j+1) -> y dir
1027+
(i+1,j) - (i+1,j+1)
1028+
|
1029+
v
1030+
x dir
1031+
1032+
in flatten coordinates:
1033+
i*ny+j - i*ny+j+1
1034+
(i+1)*ny+j - (i+1)*ny+j+1
1035+
"""
1036+
1037+
t1 = (i * ny + j,
1038+
(i + 1) % nx * ny + j,
1039+
(i + 1) % nx * ny + (j + 1) % ny)
1040+
t2 = (i * ny + j,
1041+
(i + 1) % nx * ny + (j + 1) % ny,
1042+
i * ny + (j + 1) % ny)
1043+
1044+
"""
1045+
%nx and %ny are used for wrapx and wrapy :
1046+
if (i+1)=nx => (i+1)%nx=0 => close mesh in x direction
1047+
if (j+1)=ny => (j+1)%ny=0 => close mesh in y direction
1048+
"""
1049+
1050+
nt = len(t1[0])
1051+
1052+
triangles = np.zeros((nt * 2, 3), dtype=np.uint32)
1053+
triangles[0::2, 0], triangles[0::2, 1], triangles[0::2, 2] = t1
1054+
triangles[1::2, 0], triangles[1::2, 1], triangles[1::2, 2] = t2
1055+
1056+
lines = np.zeros((nt * 4, 2), dtype=np.uint32)
1057+
lines[::4,0], lines[::4,1] = t1[:2]
1058+
lines[1::4,0], lines[1::4,1] = t1[0],t2[2]
1059+
lines[2::4,0], lines[2::4,1] = t2[2:0:-1]
1060+
lines[3::4,0], lines[3::4,1] = t1[1],t2[1]
1061+
1062+
return triangles, lines
1063+

notebooks/Mesh speed comparison.ipynb

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"metadata": {
7+
"collapsed": true
8+
},
9+
"outputs": [],
10+
"source": [
11+
"import numpy as np\n",
12+
"import ipyvolume.pylab as p3\n",
13+
"from numpy.testing import assert_array_equal"
14+
]
15+
},
16+
{
17+
"cell_type": "code",
18+
"execution_count": 8,
19+
"metadata": {
20+
"scrolled": false
21+
},
22+
"outputs": [],
23+
"source": [
24+
"from ipyvolume.pylab import make_triangles_lines as new_make_triangles_lines\n",
25+
"def old_make_triangles_lines(x,y,z,wrapx=False,wrapy=False):\n",
26+
" def dim(x):\n",
27+
" d = 0\n",
28+
" el = x\n",
29+
" while True:\n",
30+
" try:\n",
31+
" el = el[0]\n",
32+
" d += 1\n",
33+
" except Exception:\n",
34+
" break\n",
35+
" return d\n",
36+
"\n",
37+
" if dim(x) == 2:\n",
38+
" nx, ny = shape = x.shape\n",
39+
" else:\n",
40+
" nx, ny = shape = x[0].shape\n",
41+
"\n",
42+
" def reshape(ar):\n",
43+
" if dim(ar) == 3:\n",
44+
" return [k.reshape(-1) for k in ar]\n",
45+
" else:\n",
46+
" return ar.reshape(-1)\n",
47+
"\n",
48+
" x = reshape(x)\n",
49+
" y = reshape(y)\n",
50+
" z = reshape(z)\n",
51+
"\n",
52+
" mx = nx if wrapx else nx - 1\n",
53+
" my = ny if wrapy else ny - 1\n",
54+
" triangles = np.zeros(((mx) * (my) * 2, 3), dtype=np.uint32)\n",
55+
" lines = np.zeros(((mx) * (my) * 4, 2), dtype=np.uint32)\n",
56+
" \n",
57+
" \n",
58+
" \n",
59+
" \n",
60+
" def index_from2d(i, j):\n",
61+
" xi = (i % nx)\n",
62+
" yi = (j % ny)\n",
63+
" return ny * xi + yi\n",
64+
" \"\"\"\n",
65+
" ^ ydir\n",
66+
" |\n",
67+
" 2 3\n",
68+
" 0 1 ---> x dir\n",
69+
" \"\"\"\n",
70+
" \n",
71+
" for i in range(mx):\n",
72+
" for j in range(my):\n",
73+
" p0 = index_from2d(i, j)\n",
74+
" p1 = index_from2d(i + 1, j)\n",
75+
" p2 = index_from2d(i, j + 1)\n",
76+
" p3 = index_from2d(i + 1, j + 1)\n",
77+
" triangle_index = (i * my) + j\n",
78+
" triangles[triangle_index * 2 + 0, :] = [p0, p1, p3]\n",
79+
" triangles[triangle_index * 2 + 1, :] = [p0, p3, p2]\n",
80+
" lines[triangle_index * 4 + 0, :] = [p0, p1]\n",
81+
" lines[triangle_index * 4 + 1, :] = [p0, p2]\n",
82+
" lines[triangle_index * 4 + 2, :] = [p2, p3]\n",
83+
" lines[triangle_index * 4 + 3, :] = [p1, p3]\n",
84+
" \n",
85+
" return triangles, lines\n",
86+
" "
87+
]
88+
},
89+
{
90+
"cell_type": "code",
91+
"execution_count": 12,
92+
"metadata": {},
93+
"outputs": [],
94+
"source": [
95+
"\n",
96+
"assert_array_equal(tri_new,tri_old)\n",
97+
"assert_array_equal(line_new,line_old)"
98+
]
99+
},
100+
{
101+
"cell_type": "code",
102+
"execution_count": 14,
103+
"metadata": {},
104+
"outputs": [
105+
{
106+
"name": "stdout",
107+
"output_type": "stream",
108+
"text": [
109+
"330 µs ± 574 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
110+
"44.9 ms ± 135 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
111+
]
112+
}
113+
],
114+
"source": [
115+
"# Small data set.\n",
116+
"nx = 100\n",
117+
"ny = 50\n",
118+
"X = np.linspace(-5, 5, nx)\n",
119+
"Y = np.linspace(-5, 5, ny)\n",
120+
"X, Y = np.meshgrid(X, Y, indexing='xy')\n",
121+
"R = np.sqrt(X**2 + Y**2)\n",
122+
"Z = np.sin(R)\n",
123+
"\n",
124+
"tri_new, line_new = new_make_triangles_lines(X,Y,Z)\n",
125+
"tri_old, line_old = old_make_triangles_lines(X,Y,Z)\n",
126+
"\n",
127+
"assert_array_equal(tri_new,tri_old)\n",
128+
"assert_array_equal(line_new,line_old)\n",
129+
"\n",
130+
"\n",
131+
"%timeit new_make_triangles_lines(X,Y,Z)\n",
132+
"%timeit old_make_triangles_lines(X,Y,Z)\n"
133+
]
134+
},
135+
{
136+
"cell_type": "code",
137+
"execution_count": 16,
138+
"metadata": {},
139+
"outputs": [
140+
{
141+
"name": "stdout",
142+
"output_type": "stream",
143+
"text": [
144+
"12.4 ms ± 80.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
145+
"1.56 s ± 2.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
146+
]
147+
}
148+
],
149+
"source": [
150+
"# Larger data set.\n",
151+
"nx = 400\n",
152+
"ny = 425\n",
153+
"X = np.linspace(-5, 5, nx)\n",
154+
"Y = np.linspace(-5, 5, ny)\n",
155+
"X, Y = np.meshgrid(X, Y, indexing='xy')\n",
156+
"R = np.sqrt(X**2 + Y**2)\n",
157+
"Z = np.sin(R)\n",
158+
"\n",
159+
"tri_new, line_new = new_make_triangles_lines(X,Y,Z)\n",
160+
"tri_old, line_old = old_make_triangles_lines(X,Y,Z)\n",
161+
"\n",
162+
"assert_array_equal(tri_new,tri_old)\n",
163+
"assert_array_equal(line_new,line_old)\n",
164+
"\n",
165+
"%timeit tri_new, line_new = new_make_triangles_lines(X,Y,Z)\n",
166+
"%timeit tri_old, line_old = old_make_triangles_lines(X,Y,Z)"
167+
]
168+
},
169+
{
170+
"cell_type": "code",
171+
"execution_count": null,
172+
"metadata": {
173+
"collapsed": true
174+
},
175+
"outputs": [],
176+
"source": []
177+
}
178+
],
179+
"metadata": {
180+
"kernelspec": {
181+
"display_name": "Python 3",
182+
"language": "python",
183+
"name": "python3"
184+
},
185+
"language_info": {
186+
"codemirror_mode": {
187+
"name": "ipython",
188+
"version": 3
189+
},
190+
"file_extension": ".py",
191+
"mimetype": "text/x-python",
192+
"name": "python",
193+
"nbconvert_exporter": "python",
194+
"pygments_lexer": "ipython3",
195+
"version": "3.6.1"
196+
}
197+
},
198+
"nbformat": 4,
199+
"nbformat_minor": 2
200+
}

0 commit comments

Comments
 (0)