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

Skip to content

Commit 49b1de2

Browse files
Oblimanscottshambaugh
Obliman
authored andcommitted
Update art3d.py
This update changes the depthscale behavior from the original Normalize method to an improved version that behaves well even when points are closely spaced. Update art3d.py Fixed line lengths, added handling of zero-length datasets when computing the data scale. Updated art3d.py per feedback Renamed get_data_scale and dscl to indicate that they are private, added description of function's purpose, and replaced np.power with np.sqrt. Update art3d.py Forgot this portion of the update earlier. Fixes the alpha draw order flipping as the point depth inverts even though the alpha should not change per point. Update art3d.py per reviewer feedback Only one dataset of (X, Y, Z) needs to be checked for zero length assuming all have the same length. Instead of using a small constant in the denominator it's cleaner to just check for '_dscl==0' and return ones instead. This applies for datasets with no datapoints, all the same datapoint such that no scale can be estimated. Update art3d.py per reviewer feedback Fixed some word wrapping, made get_data_scale more compact, replaced _dscl with the more descriptive _data_scale, removed redundant comments, added clearer comments for implementation of shading modes
1 parent cc82d4f commit 49b1de2

File tree

1 file changed

+84
-13
lines changed

1 file changed

+84
-13
lines changed

lib/mpl_toolkits/mplot3d/art3d.py

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,18 @@ def get_edgecolor(self):
737737
return self._maybe_depth_shade_and_sort_colors(super().get_edgecolor())
738738

739739

740+
def _get_data_scale(X, Y, Z):
741+
"""
742+
Estimate the scale of the 3D data for use in depth shading
743+
"""
744+
# Account for empty datasets. Assume that X Y and Z have equal lengths
745+
if len(X) == 0:
746+
return 0
747+
748+
# Estimate the scale using the RSS of the ranges of the dimensions
749+
return np.sqrt(np.ptp(X)**2 + np.ptp(Y)**2 + np.ptp(Z)**2)
750+
751+
740752
class Path3DCollection(PathCollection):
741753
"""
742754
A collection of 3D paths.
@@ -858,6 +870,7 @@ def do_3d_projection(self):
858870
vxs, vys, vzs, vis = proj3d._proj_transform_clip(xs, ys, zs,
859871
self.axes.M,
860872
self.axes._focal_length)
873+
self._data_scale = _get_data_scale(vxs, vys, vzs)
861874
# Sort the points based on z coordinates
862875
# Performance optimization: Create a sorted index array and reorder
863876
# points and point properties according to the index array
@@ -902,14 +915,18 @@ def _use_zordered_offset(self):
902915
self._offsets = old_offset
903916

904917
def _maybe_depth_shade_and_sort_colors(self, color_array):
905-
color_array = (
906-
_zalpha(color_array, self._vzs)
907-
if self._vzs is not None and self._depthshade
908-
else color_array
909-
)
918+
# Adjust the color_array alpha values if point depths are defined
919+
# and depth shading is active
920+
if self._vzs is not None and self._depthshade:
921+
color_array = _zalpha(color_array, self._vzs,
922+
_data_scale=self._data_scale)
923+
924+
# Adjust the order of the color_array using the _z_markers_idx,
925+
# which has been sorted by z-depth
910926
if len(color_array) > 1:
911927
color_array = color_array[self._z_markers_idx]
912-
return mcolors.to_rgba_array(color_array, self._alpha)
928+
929+
return mcolors.to_rgba_array(color_array)
913930

914931
def get_facecolor(self):
915932
return self._maybe_depth_shade_and_sort_colors(super().get_facecolor())
@@ -1290,17 +1307,71 @@ def rotate_axes(xs, ys, zs, zdir):
12901307
return xs, ys, zs
12911308

12921309

1293-
def _zalpha(colors, zs):
1310+
def _zalpha(colors, zs, _data_scale=None, shading_mode=0):
12941311
"""Modify the alphas of the color list according to depth."""
1295-
# FIXME: This only works well if the points for *zs* are well-spaced
1296-
# in all three dimensions. Otherwise, at certain orientations,
1297-
# the min and max zs are very close together.
1298-
# Should really normalize against the viewing depth.
1312+
12991313
if len(colors) == 0 or len(zs) == 0:
13001314
return np.zeros((0, 4))
1301-
norm = Normalize(min(zs), max(zs))
1302-
sats = 1 - norm(zs) * 0.7
1315+
1316+
if _data_scale is None:
1317+
# This only works well if the points for *zs* are well-spaced
1318+
# in all three dimensions. Otherwise, at certain orientations,
1319+
# the min and max zs are very close together.
1320+
# Should really normalize against the viewing depth.
1321+
1322+
# Normalize the z-depths to the range 0 - 1
1323+
norm = Normalize(min(zs), max(zs))
1324+
1325+
# Generate alpha multipliers using the normalized z-depths so that
1326+
# closer points are opaque and the furthest points are still visible,
1327+
# but transparent
1328+
sats = 1 - norm(zs) * 0.7
1329+
1330+
else:
1331+
# Improved normalization using a scale value derived from the XYZ
1332+
# limits of the plot
1333+
1334+
if _data_scale == 0:
1335+
# Don't scale the alpha values since we have no valid
1336+
# data scale for reference
1337+
sats = np.ones_like(zs)
1338+
1339+
else:
1340+
if shading_mode == 0:
1341+
# This is the mode that most closely matches the behavior
1342+
# when _data_scale = None
1343+
1344+
# Shallower points have an increasingly solid appearance
1345+
# Deeper points have an increasingly transparent appearance
1346+
# Points with uniform depth have a solid appearance
1347+
sats = np.clip(1 - (zs - min(zs)) / _data_scale, 0.3, 1)
1348+
1349+
elif shading_mode == 1:
1350+
# Shallower points have an increasingly solid appearance
1351+
# Deeper points have an increasingly transparent appearance
1352+
# Points with uniform depth have a transparent appearance
1353+
sats = np.clip((max(zs) - zs) / _data_scale, 0.3, 1)
1354+
1355+
elif shading_mode == 2:
1356+
# Shallower points have an increasingly transparent appearance
1357+
# Deeper points have an increasingly solid appearance
1358+
# Points with uniform depth have a solid appearance
1359+
sats = np.clip(1 - (max(zs) - zs) / _data_scale, 0.3, 1)
1360+
1361+
elif shading_mode == 3:
1362+
# Shallower points have an increasingly transparent appearance
1363+
# Deeper points have an increasingly solid appearance
1364+
# Points with uniform depth have a transparent appearance
1365+
sats = np.clip((zs - min(zs)) / _data_scale, 0.3, 1)
1366+
1367+
else:
1368+
raise NotImplementedError(
1369+
'Specified "shading_mode" must be 0 (default), 1, 2, or 3')
1370+
13031371
rgba = np.broadcast_to(mcolors.to_rgba_array(colors), (len(zs), 4))
1372+
1373+
# Change the alpha values of the colors using the generated alpha
1374+
# multipliers
13041375
return np.column_stack([rgba[:, :3], rgba[:, 3] * sats])
13051376

13061377

0 commit comments

Comments
 (0)