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

Skip to content

Commit f6ee9a0

Browse files
committed
Add shading to Axes3D.voxels, and enable it by default
This also required slightly more care about face ordering, hence the extra square variables.
1 parent de38ef2 commit f6ee9a0

File tree

8 files changed

+74
-10
lines changed

8 files changed

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

lib/mpl_toolkits/mplot3d/axes3d.py

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

27302730
quiver3D = quiver
27312731

2732-
def voxels(self, *args, facecolors=None, edgecolors=None, **kwargs):
2732+
def voxels(self, *args, facecolors=None, edgecolors=None, shade=True,
2733+
lightsource=None, **kwargs):
27332734
"""
27342735
ax.voxels([x, y, z,] /, filled, **kwargs)
27352736
@@ -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)