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

Skip to content

Commit d5562e9

Browse files
Complete rework of 3D depthshading
Remove legacy and inverted depth shade options Remove depthshade getters getter make depthshade_minalpha kwarg only kwarg order getter Update image test failures Linting fix masked scatter depth shading removed depthshade check fix tests Fix build warning
1 parent f772eeb commit d5562e9

File tree

10 files changed

+37
-153
lines changed

10 files changed

+37
-153
lines changed
Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
1-
Depth-shading fix and more depth-shading options
2-
--------------------------------------------------------------
3-
4-
New options have been added which allow users to modify the behavior of
5-
depth-shading while addressing a visual bug.
1+
3D depth-shading fix
2+
--------------------
63

74
Previously, a slightly buggy method of estimating the "depth" of plotted
85
items could lead to sudden and unexpected changes in transparency as the
96
plot orientation changed.
107

11-
Now, the behavior has been made smooth and predictable, and the user is
12-
provided with three new options: whether to invert the shading, setting the
13-
lowest acceptable alpha value (highest transparency), and whether to use
14-
the old algorithm.
15-
16-
The default behavior visually matches the old algorithm: items that appear to be
17-
"deeper" into the screen will become increasingly transparent (up to the now
18-
user-defined limit). If the inversion option is used then items will start
19-
at maximum transparency and become gradually opaque with increasing depth.
20-
21-
Note 1: depth-shading applies to Patch3DCollections and Path3DCollections,
22-
including scatter plots.
8+
Now, the behavior has been made smooth and predictable. A new parameter
9+
``depthshade_minalpha`` has also been added to allow users to set the minimum
10+
transparency level.
2311

24-
Note 2: "depthshade=True" must still be used to enable depth-shading
12+
Depth-shading is an option for Patch3DCollections and Path3DCollections,
13+
including 3D scatter plots. Depth-shading is still off by default, and
14+
``depthshade=True`` must still be used to enable it.
2515

2616
A simple example:
2717

@@ -42,9 +32,7 @@ A simple example:
4232
zs=Z,
4333
s=S,
4434
depthshade=True,
45-
depthshade_minalpha=0.1,
46-
depthshade_inverted=True,
47-
depthshade_legacy=True,
35+
depthshade_minalpha=0.3,
4836
)
4937

5038
plt.show()

lib/mpl_toolkits/mplot3d/art3d.py

Lines changed: 22 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
path as mpath)
1919
from matplotlib.collections import (
2020
Collection, LineCollection, PolyCollection, PatchCollection, PathCollection)
21-
from matplotlib.colors import Normalize
2221
from matplotlib.patches import Patch
2322
from . import proj3d
2423

@@ -649,9 +648,7 @@ def __init__(
649648
zs=0,
650649
zdir="z",
651650
depthshade=True,
652-
depthshade_inverted=False,
653651
depthshade_minalpha=0.3,
654-
depthshade_legacy=False,
655652
axlim_clip=False,
656653
**kwargs
657654
):
@@ -670,40 +667,21 @@ def __init__(
670667
give the appearance of depth (default is *True*).
671668
This is typically desired in scatter plots.
672669
673-
*depthshade_inverted* sets whether to reverse the order of
674-
depth-shading transparency.
675-
676670
*depthshade_minalpha* sets the minimum alpha value applied by
677671
depth-shading.
678-
679-
*depthshade_legacy* sets whether to use the legacy algorithm
680-
for depth-shading.
681672
"""
682673
self._depthshade = depthshade
683-
self._depthshade_inverted = depthshade_inverted
684674
self._depthshade_minalpha = depthshade_minalpha
685-
self._depthshade_legacy = depthshade_legacy
686675
super().__init__(*args, **kwargs)
687676
self.set_3d_properties(zs, zdir, axlim_clip)
688677

689678
def get_depthshade(self):
690679
return self._depthshade
691680

692-
def get_depthshade_inverted(self):
693-
return self._depthshade_inverted
694-
695-
def get_depthshade_minalpha(self):
696-
return self._depthshade_minalpha
697-
698-
def get_depthshade_legacy(self):
699-
return self._depthshade_legacy
700-
701681
def set_depthshade(
702682
self,
703683
depthshade,
704-
depthshade_inverted=False,
705684
depthshade_minalpha=0.3,
706-
depthshade_legacy=False,
707685
):
708686
"""
709687
Set whether depth shading is performed on collection members.
@@ -713,17 +691,11 @@ def set_depthshade(
713691
depthshade : bool
714692
Whether to shade the patches in order to give the appearance of
715693
depth.
716-
depthshade_inverted : bool
717-
Whether to reverse order of depth-shading transparency.
718694
depthshade_minalpha : float
719695
Sets the minimum alpha value used by depth-shading.
720-
depthshade_legacy : bool
721-
Whtether to use the legacy algorithm for depth-shading.
722696
"""
723697
self._depthshade = depthshade
724-
self._depthshade_inverted = depthshade_inverted
725698
self._depthshade_minalpha = depthshade_minalpha
726-
self._depthshade_legacy = depthshade_legacy
727699
self.stale = True
728700

729701
def set_sort_zpos(self, val):
@@ -787,9 +759,7 @@ def _maybe_depth_shade_and_sort_colors(self, color_array):
787759
_zalpha(
788760
color_array,
789761
self._vzs,
790-
inverted=self._depthshade_inverted,
791762
min_alpha=self._depthshade_minalpha,
792-
legacy=self._depthshade_legacy,
793763
)
794764
if self._vzs is not None and self._depthshade
795765
else color_array
@@ -813,13 +783,24 @@ def get_edgecolor(self):
813783
def _get_data_scale(X, Y, Z):
814784
"""
815785
Estimate the scale of the 3D data for use in depth shading
786+
787+
Parameters
788+
----------
789+
X, Y, Z : masked arrays
790+
The data to estimate the scale of.
816791
"""
817-
# Account for empty datasets. Assume that X Y and Z have equal lengths
818-
if len(X) == 0:
792+
# Account for empty datasets. Assume that X Y and Z have the same number
793+
# of elements.
794+
if not np.ma.count(X):
819795
return 0
820796

821797
# Estimate the scale using the RSS of the ranges of the dimensions
822-
return np.sqrt(np.ptp(X) ** 2 + np.ptp(Y) ** 2 + np.ptp(Z) ** 2)
798+
# Note that we don't use np.ma.ptp() because we otherwise get a build
799+
# warning about handing empty arrays.
800+
ptp_x = X.max() - X.min()
801+
ptp_y = Y.max() - Y.min()
802+
ptp_z = Z.max() - Z.min()
803+
return np.sqrt(ptp_x ** 2 + ptp_y ** 2 + ptp_z ** 2)
823804

824805

825806
class Path3DCollection(PathCollection):
@@ -833,9 +814,7 @@ def __init__(
833814
zs=0,
834815
zdir="z",
835816
depthshade=True,
836-
depthshade_inverted=False,
837817
depthshade_minalpha=0.3,
838-
depthshade_legacy=False,
839818
axlim_clip=False,
840819
**kwargs
841820
):
@@ -854,19 +833,11 @@ def __init__(
854833
give the appearance of depth (default is *True*).
855834
This is typically desired in scatter plots.
856835
857-
*depthshade_inverted* sets whether to reverse the order of
858-
depth-shading transparency.
859-
860836
*depthshade_minalpha* sets the minimum alpha value applied by
861837
depth-shading.
862-
863-
*depthshade_legacy* sets whether to use the legacy algorithm
864-
for depth-shading.
865838
"""
866839
self._depthshade = depthshade
867-
self._depthshade_inverted = depthshade_inverted
868840
self._depthshade_minalpha = depthshade_minalpha
869-
self._depthshade_legacy = depthshade_legacy
870841
self._in_draw = False
871842
super().__init__(*args, **kwargs)
872843
self.set_3d_properties(zs, zdir, axlim_clip)
@@ -945,21 +916,10 @@ def set_linewidth(self, lw):
945916
def get_depthshade(self):
946917
return self._depthshade
947918

948-
def get_depthshade_inverted(self):
949-
return self._depthshade_inverted
950-
951-
def get_depthshade_minalpha(self):
952-
return self._depthshade_minalpha
953-
954-
def get_depthshade_legacy(self):
955-
return self._depthshade_legacy
956-
957919
def set_depthshade(
958920
self,
959921
depthshade,
960-
depthshade_inverted=False,
961922
depthshade_minalpha=0.3,
962-
depthshade_legacy=False,
963923
):
964924
"""
965925
Set whether depth shading is performed on collection members.
@@ -969,17 +929,11 @@ def set_depthshade(
969929
depthshade : bool
970930
Whether to shade the patches in order to give the appearance of
971931
depth.
972-
depthshade_inverted : bool
973-
Whether to reverse order of depth-shading transparency.
974932
depthshade_minalpha : float
975933
Sets the minimum alpha value used by depth-shading.
976-
depthshade_legacy : bool
977-
Whtether to use the legacy algorithm for depth-shading.
978934
"""
979935
self._depthshade = depthshade
980-
self._depthshade_inverted = depthshade_inverted
981936
self._depthshade_minalpha = depthshade_minalpha
982-
self._depthshade_legacy = depthshade_legacy
983937
self.stale = True
984938

985939
def do_3d_projection(self):
@@ -1048,9 +1002,7 @@ def _maybe_depth_shade_and_sort_colors(self, color_array):
10481002
color_array = _zalpha(
10491003
color_array,
10501004
self._vzs,
1051-
inverted=self._depthshade_inverted,
10521005
min_alpha=self._depthshade_minalpha,
1053-
legacy=self._depthshade_legacy,
10541006
_data_scale=self._data_scale,
10551007
)
10561008

@@ -1078,10 +1030,9 @@ def patch_collection_2d_to_3d(
10781030
zs=0,
10791031
zdir="z",
10801032
depthshade=True,
1081-
depthshade_inverted=False,
1082-
depthshade_minalpha=0.3,
1083-
depthshade_legacy=False,
10841033
axlim_clip=False,
1034+
*args,
1035+
depthshade_minalpha=0.3
10851036
):
10861037
"""
10871038
Convert a `.PatchCollection` into a `.Patch3DCollection` object
@@ -1100,25 +1051,18 @@ def patch_collection_2d_to_3d(
11001051
See `.get_dir_vector` for a description of the values.
11011052
depthshade
11021053
Whether to shade the patches to give a sense of depth. Default: *True*.
1103-
depthshade_invert
1104-
Whether to reverse order of depth-shading transparency. Default: *False*.
11051054
depthshade_minalpha
11061055
Sets the minimum alpha value used by depth-shading. Default: 0.3.
1107-
depthshade_legacy
1108-
Whether to use the legacy algorithm for depth-shading. Default: *False*.
11091056
axlim_clip : bool, default: False
11101057
Whether to hide patches with a vertex outside the axes view limits.
1111-
11121058
"""
11131059
if isinstance(col, PathCollection):
11141060
col.__class__ = Path3DCollection
11151061
col._offset_zordered = None
11161062
elif isinstance(col, PatchCollection):
11171063
col.__class__ = Patch3DCollection
11181064
col._depthshade = depthshade
1119-
col._depthshade_inverted = depthshade_inverted
11201065
col._depthshade_minalpha = depthshade_minalpha
1121-
col._depthshade_legacy = depthshade_legacy
11221066
col._in_draw = False
11231067
col.set_3d_properties(zs, zdir, axlim_clip)
11241068

@@ -1502,9 +1446,7 @@ def rotate_axes(xs, ys, zs, zdir):
15021446
def _zalpha(
15031447
colors,
15041448
zs,
1505-
inverted=False,
15061449
min_alpha=0.3,
1507-
legacy=False,
15081450
_data_scale=None,
15091451
):
15101452
"""Modify the alphas of the color list according to depth."""
@@ -1515,49 +1457,17 @@ def _zalpha(
15151457
# Alpha values beyond the range 0-1 inclusive make no sense, so clip them
15161458
min_alpha = np.clip(min_alpha, 0, 1)
15171459

1518-
if _data_scale is None or legacy:
1519-
# Revert to "legacy mode" if the new method of calculating
1520-
# _data_scale fails, or if the user asks for it
1521-
1522-
# This only works well if the points for *zs* are well-spaced in
1523-
# all three dimensions. Otherwise, at certain orientations the
1524-
# min and max zs are very close together.
1525-
# Should really normalize against the viewing depth.
1526-
1527-
# Normalize the z-depths to the range 0 - 1
1528-
norm = Normalize(np.min(zs), np.max(zs))
1529-
1530-
# Generate alpha multipliers using the normalized z-depths so that
1531-
# closer points are opaque and the furthest points are still visible,
1532-
# but transparent
1533-
if inverted:
1534-
sats = norm(zs) * (1 - min_alpha) + min_alpha
1535-
else:
1536-
sats = 1 - norm(zs) * (1 - min_alpha)
1460+
if _data_scale is None or _data_scale == 0:
1461+
# Don't scale the alpha values since we have no valid data scale for reference
1462+
sats = np.ones_like(zs)
15371463

15381464
else:
1539-
# Improved normalization using a scale value derived from the XYZ
1540-
# limits of the plot
1541-
1542-
if _data_scale == 0:
1543-
# Don't scale the alpha values since we have no valid
1544-
# data scale for reference
1545-
sats = np.ones_like(zs)
1546-
1547-
else:
1548-
if inverted:
1549-
# Deeper points have an increasingly solid appearance
1550-
sats = np.clip(1 - (np.max(zs) - zs) / _data_scale, min_alpha, 1)
1551-
else:
1552-
# This is the mode that most closely matches the legacy behavior
1553-
1554-
# Deeper points have an increasingly transparent appearance
1555-
sats = np.clip(1 - (zs - np.min(zs)) / _data_scale, min_alpha, 1)
1465+
# Deeper points have an increasingly transparent appearance
1466+
sats = np.clip(1 - (zs - np.min(zs)) / _data_scale, min_alpha, 1)
15561467

15571468
rgba = np.broadcast_to(mcolors.to_rgba_array(colors), (len(zs), 4))
15581469

1559-
# Change the alpha values of the colors using the generated alpha
1560-
# multipliers
1470+
# Change the alpha values of the colors using the generated alpha multipliers
15611471
return np.column_stack([rgba[:, :3], rgba[:, 3] * sats])
15621472

15631473

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2908,10 +2908,8 @@ def add_collection3d(self, col, zs=0, zdir='z', autolim=True, *,
29082908
"edgecolors", "c", "facecolor",
29092909
"facecolors", "color"])
29102910
def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True,
2911-
depthshade_inverted=False,
2911+
*args,
29122912
depthshade_minalpha=0.3,
2913-
depthshade_legacy=False,
2914-
*args,
29152913
axlim_clip=False,
29162914
**kwargs):
29172915
"""
@@ -2950,15 +2948,9 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True,
29502948
depth. Each call to ``scatter()`` will perform its depthshading
29512949
independently.
29522950
2953-
depthshade_inverted : bool, default: False
2954-
Whether to reverse the order of depth-shading transparency.
2955-
29562951
depthshade_minalpha : float, default: 0.3
29572952
The lowest alpha value applied by depth-shading.
29582953
2959-
depthshade_legacy : bool, default: False
2960-
Whether to use the legacy algorithm for depth-shading.
2961-
29622954
axlim_clip : bool, default: False
29632955
Whether to hide the scatter points outside the axes view limits.
29642956
@@ -2997,12 +2989,9 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True,
29972989
zs=zs,
29982990
zdir=zdir,
29992991
depthshade=depthshade,
3000-
depthshade_inverted=depthshade_inverted,
30012992
depthshade_minalpha=depthshade_minalpha,
3002-
depthshade_legacy=depthshade_legacy,
30032993
axlim_clip=axlim_clip,
3004-
)
3005-
2994+
)
30062995
if self._zmargin < 0.05 and xs.size > 0:
30072996
self.set_zmargin(0.05)
30082997

0 commit comments

Comments
 (0)