Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 50d9070

Browse files
Add support for x,y,z arguments
1 parent b16f70a commit 50d9070

File tree

6 files changed

+200
-20
lines changed

6 files changed

+200
-20
lines changed

‎examples/mplot3d/voxels_numpy_logo.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'''
2+
===============================
3+
3D voxel plot of the numpy logo
4+
===============================
5+
6+
Demonstrates using ``ax.voxels`` with uneven coordinates
7+
'''
8+
import matplotlib.pyplot as plt
9+
import numpy as np
10+
from mpl_toolkits.mplot3d import Axes3D
11+
12+
13+
def explode(data):
14+
size = np.array(data.shape)*2
15+
data_e = np.zeros(size - 1, dtype=data.dtype)
16+
data_e[::2, ::2, ::2] = data
17+
return data_e
18+
19+
# build up the numpy logo
20+
n_voxels = np.zeros((4, 3, 4), dtype=bool)
21+
n_voxels[0, 0, :] = True
22+
n_voxels[-1, 0, :] = True
23+
n_voxels[1, 0, 2] = True
24+
n_voxels[2, 0, 1] = True
25+
facecolors = np.where(n_voxels, '#FFD65DC0', '#7A88CCC0')
26+
edgecolors = np.where(n_voxels, '#BFAB6E', '#7D84A6')
27+
filled = np.ones(n_voxels.shape)
28+
29+
# upscale the above voxel image, leaving gaps
30+
filled_2 = explode(filled)
31+
fcolors_2 = explode(facecolors)
32+
ecolors_2 = explode(edgecolors)
33+
34+
# Shrink the gaps
35+
x, y, z = np.indices(np.array(filled_2.shape) + 1).astype(float) // 2
36+
x[0::2, :, :] += 0.05
37+
y[:, 0::2, :] += 0.05
38+
z[:, :, 0::2] += 0.05
39+
x[1::2, :, :] += 0.95
40+
y[:, 1::2, :] += 0.95
41+
z[:, :, 1::2] += 0.95
42+
43+
fig = plt.figure()
44+
ax = fig.gca(projection='3d')
45+
ax.voxels(x, y, z, filled_2, facecolors=fcolors_2, edgecolors=ecolors_2)
46+
47+
plt.show()

‎examples/mplot3d/voxels_rgb.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,36 @@
1010
import numpy as np
1111
from mpl_toolkits.mplot3d import Axes3D
1212

13+
14+
def midpoints(x):
15+
sl = ()
16+
for i in range(x.ndim):
17+
x = (x[sl + np.index_exp[:-1]] + x[sl + np.index_exp[1:]]) / 2.0
18+
sl += np.index_exp[:]
19+
return x
20+
1321
# prepare some coordinates, and attach rgb values to each
14-
x, y, z = np.indices((16, 16, 16))
15-
r = (x+0.5) /16
16-
g = (y+0.5) /16
17-
b = (z+0.5) /16
22+
r, g, b = np.indices((17, 17, 17)) /16.0
23+
rc = midpoints(r)
24+
gc = midpoints(g)
25+
bc = midpoints(b)
1826

1927
# define a sphere about [0.5, 0.5, 0.5]
20-
sphere = (r - 0.5)**2 + (g - 0.5)**2 + (b - 0.5)**2 < 0.5**2
28+
sphere = (rc - 0.5)**2 + (gc - 0.5)**2 + (bc - 0.5)**2 < 0.5**2
2129

2230
# combine the color components
2331
colors = np.zeros(sphere.shape + (3,))
24-
colors[..., 0] = r
25-
colors[..., 1] = g
26-
colors[..., 2] = b
32+
colors[..., 0] = rc
33+
colors[..., 1] = gc
34+
colors[..., 2] = bc
2735

2836
# and plot everything
2937
fig = plt.figure()
3038
ax = fig.gca(projection='3d')
31-
ax.voxels(sphere,
39+
ax.voxels(r, g, b, sphere,
3240
facecolors=colors,
3341
edgecolors=np.clip(2*colors - 0.5, 0, 1), # brighter
3442
linewidth=0.5)
43+
ax.set(xlabel='r', ylabel='g', zlabel='b')
3544

3645
plt.show()

‎examples/mplot3d/voxels_torus.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'''
2+
=======================================================
3+
3D voxel / volumetric plot with cylindrical coordinates
4+
=======================================================
5+
6+
Demonstrates using the ``x, y, z`` arguments of ``ax.voxels``.
7+
'''
8+
9+
import matplotlib.pyplot as plt
10+
import matplotlib.colors
11+
import numpy as np
12+
from mpl_toolkits.mplot3d import Axes3D
13+
14+
15+
def midpoints(x):
16+
sl = ()
17+
for i in range(x.ndim):
18+
x = (x[sl + np.index_exp[:-1]] + x[sl + np.index_exp[1:]]) / 2.0
19+
sl += np.index_exp[:]
20+
return x
21+
22+
# prepare some coordinates, and attach rgb values to each
23+
r, theta, z = np.mgrid[0:1:11j, 0:np.pi*2:25j, -0.5:0.5:11j]
24+
x = r*np.cos(theta)
25+
y = r*np.sin(theta)
26+
27+
rc, thetac, zc = midpoints(r), midpoints(theta), midpoints(z)
28+
29+
# define a wobbly torus about [0.7, *, 0]
30+
sphere = (rc - 0.7)**2 + (zc + 0.2*np.cos(thetac*2))**2 < 0.2**2
31+
32+
# combine the color components
33+
hsv = np.zeros(sphere.shape + (3,))
34+
hsv[..., 0] = thetac / (np.pi*2)
35+
hsv[..., 1] = rc
36+
hsv[..., 2] = zc + 0.5
37+
colors = matplotlib.colors.hsv_to_rgb(hsv)
38+
39+
# and plot everything
40+
fig = plt.figure()
41+
ax = fig.gca(projection='3d')
42+
ax.voxels(x, y, z, sphere,
43+
facecolors=colors,
44+
edgecolors=np.clip(2*colors - 0.5, 0, 1), # brighter
45+
linewidth=0.5)
46+
47+
plt.show()

‎lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2746,8 +2746,10 @@ def calc_arrow(uvw, angle=15):
27462746

27472747
quiver3D = quiver
27482748

2749-
def voxels(self, filled, **kwargs):
2749+
def voxels(self, *args, **kwargs):
27502750
"""
2751+
ax.voxels([x, y, z,] /, filled, **kwargs)
2752+
27512753
Plot a set of filled voxels
27522754
27532755
All voxels are plotted as 1x1x1 cubes on the axis, with filled[0,0,0]
@@ -2758,10 +2760,16 @@ def voxels(self, filled, **kwargs):
27582760
27592761
Parameters
27602762
----------
2761-
filled : np.array
2763+
filled : 3D np.array of bool
27622764
A 3d array of values, with truthy values indicating which voxels
27632765
to fill
27642766
2767+
x, y, z : 3D np.array, optional
2768+
The coordinates of the corners of the voxels. This should broadcast
2769+
to a shape one larger in every dimension than the shape of `filled`.
2770+
These arguments can be used to plot non-cubic voxels.
2771+
If not specified, defaults to increasing integers along each axis.
2772+
27652773
facecolors, edgecolors : array_like
27662774
The color to draw the faces and edges of the voxels. This parameter
27672775
can be:
@@ -2791,10 +2799,33 @@ def voxels(self, filled, **kwargs):
27912799
27922800
.. plot:: gallery/mplot3d/voxels.py
27932801
.. plot:: gallery/mplot3d/voxels_rgb.py
2802+
.. plot:: gallery/mplot3d/voxels_torus.py
2803+
.. plot:: gallery/mplot3d/voxels_numpy_logo.py
27942804
"""
2805+
2806+
# work out which signature we should be using, and use it to parse
2807+
# the arguments. Name must be voxels for the correct error message
2808+
if len(args) >= 3:
2809+
# underscores indicate position only
2810+
def voxels(__x, __y, __z, filled, **kwargs):
2811+
return (__x, __y, __z), filled, kwargs
2812+
else:
2813+
def voxels(filled, **kwargs):
2814+
return None, filled, kwargs
2815+
2816+
xyz, filled, kwargs = voxels(*args, **kwargs)
2817+
27952818
# check dimensions
27962819
if filled.ndim != 3:
27972820
raise ValueError("Argument filled must be 3-dimensional")
2821+
size = np.array(filled.shape, dtype=np.intp)
2822+
2823+
# check xyz coordinates, which are one larger than the filled shape
2824+
coord_shape = tuple(size + 1)
2825+
if xyz is None:
2826+
x, y, z = np.indices(coord_shape)
2827+
else:
2828+
x, y, z = (broadcast_to(c, coord_shape) for c in xyz)
27982829

27992830
def _broadcast_color_arg(color, name):
28002831
if np.ndim(color) in (0, 1):
@@ -2821,25 +2852,21 @@ def _broadcast_color_arg(color, name):
28212852
edgecolors = _broadcast_color_arg(edgecolors, 'edgecolors')
28222853

28232854
# always scale to the full array, even if the data is only in the center
2824-
self.auto_scale_xyz(
2825-
[0, filled.shape[0]],
2826-
[0, filled.shape[1]],
2827-
[0, filled.shape[2]]
2828-
)
2855+
self.auto_scale_xyz(x, y, z)
28292856

28302857
# points lying on corners of a square
28312858
square = np.array([
28322859
[0, 0, 0],
28332860
[0, 1, 0],
28342861
[1, 1, 0],
28352862
[1, 0, 0]
2836-
])
2863+
], dtype=np.intp)
28372864

28382865
voxel_faces = defaultdict(list)
28392866

28402867
def permutation_matrices(n):
28412868
""" Generator of cyclic permutation matices """
2842-
mat = np.eye(n, dtype=int)
2869+
mat = np.eye(n, dtype=np.intp)
28432870
for i in range(n):
28442871
yield mat
28452872
mat = np.roll(mat, 1, axis=0)
@@ -2848,7 +2875,7 @@ def permutation_matrices(n):
28482875
# render
28492876
for permute in permutation_matrices(3):
28502877
# find the set of ranges to iterate over
2851-
pc, qc, rc = permute.T.dot(filled.shape[:3])
2878+
pc, qc, rc = permute.T.dot(size)
28522879
pinds = np.arange(pc)
28532880
qinds = np.arange(qc)
28542881
rinds = np.arange(rc)
@@ -2890,7 +2917,20 @@ def permutation_matrices(n):
28902917

28912918
# iterate over the faces, and generate a Poly3DCollection for each voxel
28922919
polygons = {}
2893-
for coord, faces in voxel_faces.items():
2920+
for coord, faces_inds in voxel_faces.items():
2921+
# convert indices into 3D positions
2922+
if xyz is None:
2923+
faces = faces_inds
2924+
else:
2925+
faces = []
2926+
for face_inds in faces_inds:
2927+
ind = face_inds[:, 0], face_inds[:, 1], face_inds[:, 2]
2928+
face = np.empty(face_inds.shape)
2929+
face[:, 0] = x[ind]
2930+
face[:, 1] = y[ind]
2931+
face[:, 2] = z[ind]
2932+
faces.append(face)
2933+
28942934
poly = art3d.Poly3DCollection(faces,
28952935
facecolors=facecolors[coord],
28962936
edgecolors=edgecolors[coord],
119 KB
Loading[フレーム]

‎lib/mpl_toolkits/tests/test_mplot3d.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,43 @@ def test_alpha(self):
655655
assert voxels[coord], "faces returned for absent voxel"
656656
assert isinstance(poly, art3d.Poly3DCollection)
657657

658+
@image_comparison(
659+
baseline_images=['voxels-xyz'],
660+
extensions=['png'],
661+
tol=0.01
662+
)
663+
def test_xyz(self):
664+
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
665+
666+
def midpoints(x):
667+
sl = ()
668+
for i in range(x.ndim):
669+
x = (x[sl + np.index_exp[:-1]] +
670+
x[sl + np.index_exp[1:]]) / 2.0
671+
sl += np.index_exp[:]
672+
return x
673+
674+
# prepare some coordinates, and attach rgb values to each
675+
r, g, b = np.indices((17, 17, 17)) / 16.0
676+
rc = midpoints(r)
677+
gc = midpoints(g)
678+
bc = midpoints(b)
679+
680+
# define a sphere about [0.5, 0.5, 0.5]
681+
sphere = (rc - 0.5)**2 + (gc - 0.5)**2 + (bc - 0.5)**2 < 0.5**2
682+
683+
# combine the color components
684+
colors = np.zeros(sphere.shape + (3,))
685+
colors[..., 0] = rc
686+
colors[..., 1] = gc
687+
colors[..., 2] = bc
688+
689+
# and plot everything
690+
ax.voxels(r, g, b, sphere,
691+
facecolors=colors,
692+
edgecolors=np.clip(2*colors - 0.5, 0, 1), # brighter
693+
linewidth=0.5)
694+
658695

659696
def test_inverted_cla():
660697
# Github PR #5450. Setting autoscale should reset

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /