Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aa32430

Browse files
authored
Merge pull request #13504 from meeseeksmachine/auto-backport-of-pr-13123-on-v3.1.x
Backport PR #13123 on branch v3.1.x (Add shading to Axes3D.voxels, and enable it by default)
2 parents 82741f5 + 91cfd06 commit aa32430

File tree

9 files changed

+81
-10
lines changed

9 files changed

+81
-10
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
``Axes3D.voxels`` now shades the resulting voxels
2+
-------------------------------------------------
3+
4+
See Whats new for details. The previous behavior can be achieved by passing
5+
``shade=False``.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
``Axes3D.voxels`` now shades the resulting voxels
2+
-------------------------------------------------
3+
4+
The :meth:`~mpl_toolkits.mplot3d.Axes3D.voxels` method now takes a ``shade``
5+
parameter that defaults to ``True``. This shades faces based on their
6+
orientation, behaving just like the matching parameters to
7+
:meth:`~mpl_toolkits.mplot3d.Axes3D.trisurf` and
8+
:meth:`~mpl_toolkits.mplot3d.Axes3D.bar3d`.
9+
The plot below shows how this affects the output.
10+
11+
.. plot::
12+
13+
import matplotlib.pyplot as plt
14+
import numpy as np
15+
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
16+
17+
# prepare some coordinates
18+
x, y, z = np.indices((8, 8, 8))
19+
20+
# draw cuboids in the top left and bottom right corners, and a link between them
21+
cube1 = (x < 3) & (y < 3) & (z < 3)
22+
cube2 = (x >= 5) & (y >= 5) & (z >= 5)
23+
link = abs(x - y) + abs(y - z) + abs(z - x) <= 2
24+
25+
# combine the objects into a single boolean array
26+
voxels = cube1 | cube2 | link
27+
28+
# set the colors of each object
29+
colors = np.empty(voxels.shape, dtype=object)
30+
colors[link] = 'red'
31+
colors[cube1] = 'blue'
32+
colors[cube2] = 'green'
33+
34+
# and plot everything
35+
fig = plt.figure(figsize=plt.figaspect(0.5))
36+
ax, ax_shaded = fig.subplots(1, 2, subplot_kw=dict(projection='3d'))
37+
ax.voxels(voxels, facecolors=colors, edgecolor='k', shade=False)
38+
ax.set_title("Unshaded")
39+
ax_shaded.voxels(voxels, facecolors=colors, edgecolor='k', shade=True)
40+
ax_shaded.set_title("Shaded (default)")
41+
42+
plt.show()

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2733,7 +2733,8 @@ def calc_arrow(uvw, angle=15):
27332733

27342734
quiver3D = quiver
27352735

2736-
def voxels(self, *args, facecolors=None, edgecolors=None, **kwargs):
2736+
def voxels(self, *args, facecolors=None, edgecolors=None, shade=True,
2737+
lightsource=None, **kwargs):
27372738
"""
27382739
ax.voxels([x, y, z,] /, filled, facecolors=None, edgecolors=None, \
27392740
**kwargs)
@@ -2776,6 +2777,17 @@ def voxels(self, *args, facecolors=None, edgecolors=None, **kwargs):
27762777
- A 4D ndarray of rgb/rgba data, with the components along the
27772778
last axis.
27782779
2780+
shade : bool
2781+
Whether to shade the facecolors. Defaults to True. Shading is
2782+
always disabled when *cmap* is specified.
2783+
2784+
.. versionadded:: 3.1
2785+
2786+
lightsource : `~matplotlib.colors.LightSource`
2787+
The lightsource to use when *shade* is True.
2788+
2789+
.. versionadded:: 3.1
2790+
27792791
**kwargs
27802792
Additional keyword arguments to pass onto
27812793
:func:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection`
@@ -2849,9 +2861,9 @@ def _broadcast_color_arg(color, name):
28492861
# points lying on corners of a square
28502862
square = np.array([
28512863
[0, 0, 0],
2852-
[0, 1, 0],
2864+
[1, 0, 0],
28532865
[1, 1, 0],
2854-
[1, 0, 0]
2866+
[0, 1, 0],
28552867
], dtype=np.intp)
28562868

28572869
voxel_faces = defaultdict(list)
@@ -2872,7 +2884,8 @@ def permutation_matrices(n):
28722884
qinds = np.arange(qc)
28732885
rinds = np.arange(rc)
28742886

2875-
square_rot = square.dot(permute.T)
2887+
square_rot_pos = square.dot(permute.T)
2888+
square_rot_neg = square_rot_pos[::-1]
28762889

28772890
# iterate within the current plane
28782891
for p in pinds:
@@ -2885,7 +2898,7 @@ def permutation_matrices(n):
28852898
p0 = permute.dot([p, q, 0])
28862899
i0 = tuple(p0)
28872900
if filled[i0]:
2888-
voxel_faces[i0].append(p0 + square_rot)
2901+
voxel_faces[i0].append(p0 + square_rot_neg)
28892902

28902903
# draw middle faces
28912904
for r1, r2 in zip(rinds[:-1], rinds[1:]):
@@ -2896,16 +2909,16 @@ def permutation_matrices(n):
28962909
i2 = tuple(p2)
28972910

28982911
if filled[i1] and not filled[i2]:
2899-
voxel_faces[i1].append(p2 + square_rot)
2912+
voxel_faces[i1].append(p2 + square_rot_pos)
29002913
elif not filled[i1] and filled[i2]:
2901-
voxel_faces[i2].append(p2 + square_rot)
2914+
voxel_faces[i2].append(p2 + square_rot_neg)
29022915

29032916
# draw upper faces
29042917
pk = permute.dot([p, q, rc-1])
29052918
pk2 = permute.dot([p, q, rc])
29062919
ik = tuple(pk)
29072920
if filled[ik]:
2908-
voxel_faces[ik].append(pk2 + square_rot)
2921+
voxel_faces[ik].append(pk2 + square_rot_pos)
29092922

29102923
# iterate over the faces, and generate a Poly3DCollection for each
29112924
# voxel
@@ -2924,9 +2937,20 @@ def permutation_matrices(n):
29242937
face[:, 2] = z[ind]
29252938
faces.append(face)
29262939

2940+
# shade the faces
2941+
facecolor = facecolors[coord]
2942+
edgecolor = edgecolors[coord]
2943+
if shade:
2944+
normals = self._generate_normals(faces)
2945+
facecolor = self._shade_colors(facecolor, normals, lightsource)
2946+
if edgecolor is not None:
2947+
edgecolor = self._shade_colors(
2948+
edgecolor, normals, lightsource
2949+
)
2950+
29272951
poly = art3d.Poly3DCollection(faces,
2928-
facecolors=facecolors[coord],
2929-
edgecolors=edgecolors[coord],
2952+
facecolors=facecolor,
2953+
edgecolors=edgecolor,
29302954
**kwargs
29312955
)
29322956
self.add_collection3d(poly)

0 commit comments

Comments
 (0)