From 18271f8cafeca387fdd75060ef69ae7be5d0e93e Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 18 Aug 2020 21:52:04 -0400 Subject: [PATCH 1/5] Remove redundant alpha in Path3DCollection. --- lib/mpl_toolkits/mplot3d/art3d.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index a5c923dfcd45..c505e2ace55c 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -496,12 +496,8 @@ def do_3d_projection(self, renderer): fcs = (_zalpha(self._facecolor3d, vzs) if self._depthshade else self._facecolor3d) - fcs = mcolors.to_rgba_array(fcs, self._alpha) - self.set_facecolors(fcs) - ecs = (_zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) - sizes = self._sizes3d # Sort the points based on z coordinates From 5bd536bf56f45f8c827884f7d1dd2396442efd77 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 18 Aug 2020 22:50:36 -0400 Subject: [PATCH 2/5] Correctly re-order scatter3d face/edgecolors. They should be re-ordered regardless of depth shading, but rather dependent on whether there's more than one colour. Fixes #18287. --- lib/mpl_toolkits/mplot3d/art3d.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index c505e2ace55c..75b063984494 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -509,8 +509,9 @@ def do_3d_projection(self, renderer): vzs = vzs[z_markers_idx] vxs = vxs[z_markers_idx] vys = vys[z_markers_idx] - if self._depthshade: + if len(fcs) > 1: fcs = fcs[z_markers_idx] + if len(ecs) > 1: ecs = ecs[z_markers_idx] if len(sizes) > 1: sizes = sizes[z_markers_idx] From d4f3ec3b2b8e55834225b21c0af0bae20c58675e Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 19 Aug 2020 16:57:04 -0400 Subject: [PATCH 3/5] scatter3d: Test sorting of face/edge colours. --- lib/mpl_toolkits/tests/test_mplot3d.py | 54 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index ffead3412956..e684f7a3f9d1 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -1,4 +1,5 @@ import functools +import itertools import pytest @@ -254,27 +255,44 @@ def test_scatter3d_depthshade_false(): @check_figures_equal(extensions=['png']) -def test_scatter3d_size(fig_ref, fig_test): - """Test that large markers in correct position (issue #18135)""" - x = np.arange(10) - x, y = np.meshgrid(x, x) - z = np.arange(100).reshape(10, 10) - - s = np.full(z.shape, 5) - s[0, 0] = 100 - s[-1, 0] = 100 - s[0, -1] = 100 - s[-1, -1] = 100 +def test_scatter3d_sorting(fig_ref, fig_test): + """Test that marker properties are correctly sorted.""" - ax_ref = fig_ref.gca(projection='3d') - ax_test = fig_test.gca(projection='3d') + y, x = np.mgrid[:10, :10] + z = np.arange(x.size).reshape(x.shape) + + sizes = np.full(z.shape, 25) + sizes[0::2, 0::2] = 100 + sizes[1::2, 1::2] = 100 + + facecolors = np.full(z.shape, 'C0') + facecolors[:5, :5] = 'C1' + facecolors[6:, :4] = 'C2' + facecolors[6:, 6:] = 'C3' - small = np.ma.masked_array(z, s == 100, dtype=float) - large = np.ma.masked_array(z, s != 100, dtype=float) + edgecolors = np.full(z.shape, 'C4') + edgecolors[1:5, 1:5] = 'C5' + edgecolors[5:9, 1:5] = 'C6' + edgecolors[5:9, 5:9] = 'C7' - ax_ref.scatter(x, y, large, s=100, c="C0", alpha=1) - ax_ref.scatter(x, y, small, s=5, c="C0", alpha=1) - ax_test.scatter(x, y, z, s=s, c="C0", alpha=1) + x, y, z, sizes, facecolors, edgecolors = [ + a.flatten() + for a in [x, y, z, sizes, facecolors, edgecolors] + ] + + ax_ref = fig_ref.gca(projection='3d') + sets = (np.unique(a) for a in [sizes, facecolors, edgecolors]) + for s, fc, ec in itertools.product(*sets): + subset = ( + (sizes != s) | + (facecolors != fc) | + (edgecolors != ec) + ) + subset = np.ma.masked_array(z, subset, dtype=float) + ax_ref.scatter(x, y, subset, s=s, fc=fc, ec=ec, alpha=1) + + ax_test = fig_test.gca(projection='3d') + ax_test.scatter(x, y, z, s=sizes, fc=facecolors, ec=edgecolors, alpha=1) @pytest.mark.parametrize('azim', [-50, 130]) # yellow first, blue first From 94263ac98417585be154f6e65744856ab738df43 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 19 Aug 2020 17:47:40 -0400 Subject: [PATCH 4/5] Also test scatter3d sorting with depthshade=False. --- lib/mpl_toolkits/tests/test_mplot3d.py | 27 +++++++++++++------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index e684f7a3f9d1..ad4aeed089ec 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -243,19 +243,9 @@ def test_scatter3d_color(): color='b', marker='s') -def test_scatter3d_depthshade_false(): - """ - Test that 3d scatter plot doesn't throw - IndexError with depthshade=False (issue #18037) - """ - x = y = z = np.arange(16) - fig_test = plt.figure() - ax_test = fig_test.add_subplot(projection='3d') - ax_test.scatter(x, y, z, depthshade=False) - - +@pytest.mark.parametrize('depthshade', [True, False]) @check_figures_equal(extensions=['png']) -def test_scatter3d_sorting(fig_ref, fig_test): +def test_scatter3d_sorting(fig_ref, fig_test, depthshade): """Test that marker properties are correctly sorted.""" y, x = np.mgrid[:10, :10] @@ -289,10 +279,19 @@ def test_scatter3d_sorting(fig_ref, fig_test): (edgecolors != ec) ) subset = np.ma.masked_array(z, subset, dtype=float) - ax_ref.scatter(x, y, subset, s=s, fc=fc, ec=ec, alpha=1) + + # When depth shading is disabled, the colors are passed through as + # single-item lists; this triggers single path optimization. The + # following reshaping is a hack to disable that, since the optimization + # would not occur for the full scatter which has multiple colors. + fc = np.repeat(fc, sum(~subset.mask)) + + ax_ref.scatter(x, y, subset, s=s, fc=fc, ec=ec, alpha=1, + depthshade=depthshade) ax_test = fig_test.gca(projection='3d') - ax_test.scatter(x, y, z, s=sizes, fc=facecolors, ec=edgecolors, alpha=1) + ax_test.scatter(x, y, z, s=sizes, fc=facecolors, ec=edgecolors, alpha=1, + depthshade=depthshade) @pytest.mark.parametrize('azim', [-50, 130]) # yellow first, blue first From 828c60bfac63e8489cb1ee6c7dc7767440d7e6e5 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 19 Aug 2020 17:55:09 -0400 Subject: [PATCH 5/5] Also sort line widths for scatter3d. --- lib/mpl_toolkits/mplot3d/art3d.py | 5 +++++ lib/mpl_toolkits/tests/test_mplot3d.py | 21 +++++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 75b063984494..f37be535ea10 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -488,6 +488,7 @@ def set_3d_properties(self, zs, zdir): self._facecolor3d = self.get_facecolor() self._edgecolor3d = self.get_edgecolor() self._sizes3d = self.get_sizes() + self._linewidth3d = self.get_linewidth() self.stale = True def do_3d_projection(self, renderer): @@ -499,6 +500,7 @@ def do_3d_projection(self, renderer): ecs = (_zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) sizes = self._sizes3d + lws = self._linewidth3d # Sort the points based on z coordinates # Performance optimization: Create a sorted index array and reorder @@ -515,6 +517,8 @@ def do_3d_projection(self, renderer): ecs = ecs[z_markers_idx] if len(sizes) > 1: sizes = sizes[z_markers_idx] + if len(lws) > 1: + lws = lws[z_markers_idx] vps = np.column_stack((vxs, vys)) fcs = mcolors.to_rgba_array(fcs, self._alpha) @@ -523,6 +527,7 @@ def do_3d_projection(self, renderer): self.set_edgecolors(ecs) self.set_facecolors(fcs) self.set_sizes(sizes) + self.set_linewidth(lws) PathCollection.set_offsets(self, vps) diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index ad4aeed089ec..0b4341b1185b 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -265,18 +265,23 @@ def test_scatter3d_sorting(fig_ref, fig_test, depthshade): edgecolors[5:9, 1:5] = 'C6' edgecolors[5:9, 5:9] = 'C7' - x, y, z, sizes, facecolors, edgecolors = [ + linewidths = np.full(z.shape, 2) + linewidths[0::2, 0::2] = 5 + linewidths[1::2, 1::2] = 5 + + x, y, z, sizes, facecolors, edgecolors, linewidths = [ a.flatten() - for a in [x, y, z, sizes, facecolors, edgecolors] + for a in [x, y, z, sizes, facecolors, edgecolors, linewidths] ] ax_ref = fig_ref.gca(projection='3d') - sets = (np.unique(a) for a in [sizes, facecolors, edgecolors]) - for s, fc, ec in itertools.product(*sets): + sets = (np.unique(a) for a in [sizes, facecolors, edgecolors, linewidths]) + for s, fc, ec, lw in itertools.product(*sets): subset = ( (sizes != s) | (facecolors != fc) | - (edgecolors != ec) + (edgecolors != ec) | + (linewidths != lw) ) subset = np.ma.masked_array(z, subset, dtype=float) @@ -286,12 +291,12 @@ def test_scatter3d_sorting(fig_ref, fig_test, depthshade): # would not occur for the full scatter which has multiple colors. fc = np.repeat(fc, sum(~subset.mask)) - ax_ref.scatter(x, y, subset, s=s, fc=fc, ec=ec, alpha=1, + ax_ref.scatter(x, y, subset, s=s, fc=fc, ec=ec, lw=lw, alpha=1, depthshade=depthshade) ax_test = fig_test.gca(projection='3d') - ax_test.scatter(x, y, z, s=sizes, fc=facecolors, ec=edgecolors, alpha=1, - depthshade=depthshade) + ax_test.scatter(x, y, z, s=sizes, fc=facecolors, ec=edgecolors, + lw=linewidths, alpha=1, depthshade=depthshade) @pytest.mark.parametrize('azim', [-50, 130]) # yellow first, blue first