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

Skip to content

Commit d0c8011

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 4cc7f94 commit d0c8011

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
@@ -757,6 +757,18 @@ def get_edgecolor(self):
757757
return self._maybe_depth_shade_and_sort_colors(super().get_edgecolor())
758758

759759

760+
def _get_data_scale(X, Y, Z):
761+
"""
762+
Estimate the scale of the 3D data for use in depth shading
763+
"""
764+
# Account for empty datasets. Assume that X Y and Z have equal lengths
765+
if len(X) == 0:
766+
return 0
767+
768+
# Estimate the scale using the RSS of the ranges of the dimensions
769+
return np.sqrt(np.ptp(X)**2 + np.ptp(Y)**2 + np.ptp(Z)**2)
770+
771+
760772
class Path3DCollection(PathCollection):
761773
"""
762774
A collection of 3D paths.
@@ -885,6 +897,7 @@ def do_3d_projection(self):
885897
vxs, vys, vzs, vis = proj3d._proj_transform_clip(*xyzs,
886898
self.axes.M,
887899
self.axes._focal_length)
900+
self._data_scale = _get_data_scale(vxs, vys, vzs)
888901
# Sort the points based on z coordinates
889902
# Performance optimization: Create a sorted index array and reorder
890903
# points and point properties according to the index array
@@ -929,14 +942,18 @@ def _use_zordered_offset(self):
929942
self._offsets = old_offset
930943

931944
def _maybe_depth_shade_and_sort_colors(self, color_array):
932-
color_array = (
933-
_zalpha(color_array, self._vzs)
934-
if self._vzs is not None and self._depthshade
935-
else color_array
936-
)
945+
# Adjust the color_array alpha values if point depths are defined
946+
# and depth shading is active
947+
if self._vzs is not None and self._depthshade:
948+
color_array = _zalpha(color_array, self._vzs,
949+
_data_scale=self._data_scale)
950+
951+
# Adjust the order of the color_array using the _z_markers_idx,
952+
# which has been sorted by z-depth
937953
if len(color_array) > 1:
938954
color_array = color_array[self._z_markers_idx]
939-
return mcolors.to_rgba_array(color_array, self._alpha)
955+
956+
return mcolors.to_rgba_array(color_array)
940957

941958
def get_facecolor(self):
942959
return self._maybe_depth_shade_and_sort_colors(super().get_facecolor())
@@ -1357,17 +1374,71 @@ def rotate_axes(xs, ys, zs, zdir):
13571374
return xs, ys, zs
13581375

13591376

1360-
def _zalpha(colors, zs):
1377+
def _zalpha(colors, zs, _data_scale=None, shading_mode=0):
13611378
"""Modify the alphas of the color list according to depth."""
1362-
# FIXME: This only works well if the points for *zs* are well-spaced
1363-
# in all three dimensions. Otherwise, at certain orientations,
1364-
# the min and max zs are very close together.
1365-
# Should really normalize against the viewing depth.
1379+
13661380
if len(colors) == 0 or len(zs) == 0:
13671381
return np.zeros((0, 4))
1368-
norm = Normalize(np.min(zs), np.max(zs))
1369-
sats = 1 - norm(zs) * 0.7
1382+
1383+
if _data_scale is None:
1384+
# This only works well if the points for *zs* are well-spaced
1385+
# in all three dimensions. Otherwise, at certain orientations,
1386+
# the min and max zs are very close together.
1387+
# Should really normalize against the viewing depth.
1388+
1389+
# Normalize the z-depths to the range 0 - 1
1390+
norm = Normalize(np.min(zs), np.max(zs))
1391+
1392+
# Generate alpha multipliers using the normalized z-depths so that
1393+
# closer points are opaque and the furthest points are still visible,
1394+
# but transparent
1395+
sats = 1 - norm(zs) * 0.7
1396+
1397+
else:
1398+
# Improved normalization using a scale value derived from the XYZ
1399+
# limits of the plot
1400+
1401+
if _data_scale == 0:
1402+
# Don't scale the alpha values since we have no valid
1403+
# data scale for reference
1404+
sats = np.ones_like(zs)
1405+
1406+
else:
1407+
if shading_mode == 0:
1408+
# This is the mode that most closely matches the behavior
1409+
# when _data_scale = None
1410+
1411+
# Shallower points have an increasingly solid appearance
1412+
# Deeper points have an increasingly transparent appearance
1413+
# Points with uniform depth have a solid appearance
1414+
sats = np.clip(1 - (zs - np.min(zs)) / _data_scale, 0.3, 1)
1415+
1416+
elif shading_mode == 1:
1417+
# Shallower points have an increasingly solid appearance
1418+
# Deeper points have an increasingly transparent appearance
1419+
# Points with uniform depth have a transparent appearance
1420+
sats = np.clip((np.max(zs) - zs) / _data_scale, 0.3, 1)
1421+
1422+
elif shading_mode == 2:
1423+
# Shallower points have an increasingly transparent appearance
1424+
# Deeper points have an increasingly solid appearance
1425+
# Points with uniform depth have a solid appearance
1426+
sats = np.clip(1 - (np.max(zs) - zs) / _data_scale, 0.3, 1)
1427+
1428+
elif shading_mode == 3:
1429+
# Shallower points have an increasingly transparent appearance
1430+
# Deeper points have an increasingly solid appearance
1431+
# Points with uniform depth have a transparent appearance
1432+
sats = np.clip((zs - np.min(zs)) / _data_scale, 0.3, 1)
1433+
1434+
else:
1435+
raise NotImplementedError(
1436+
'Specified "shading_mode" must be 0 (default), 1, 2, or 3')
1437+
13701438
rgba = np.broadcast_to(mcolors.to_rgba_array(colors), (len(zs), 4))
1439+
1440+
# Change the alpha values of the colors using the generated alpha
1441+
# multipliers
13711442
return np.column_stack([rgba[:, :3], rgba[:, 3] * sats])
13721443

13731444

0 commit comments

Comments
 (0)