From f93a0fc251f7aa0a8da71f92e97e54faa25b8cd7 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Wed, 16 Feb 2022 17:07:30 -0700 Subject: [PATCH] FIX: Include (0, 0) offsets in scatter autoscaling A scatter point at (0, 0) has offsets with 0s and any(offsets) then gets evaluated to False. We still want to include this point in the autoscaling if it is there. --- lib/matplotlib/collections.py | 33 ++++++++++++------------ lib/matplotlib/tests/test_collections.py | 16 ++++++++++++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 6022b4e3508a..eb05aaf00a5a 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -258,9 +258,9 @@ def get_datalim(self, transData): transform = self.get_transform() offset_trf = self.get_offset_transform() - has_offsets = np.any(self._offsets) # True if any non-zero offsets - if has_offsets and not offset_trf.contains_branch(transData): - # if there are offsets but in some coords other than data, + if not (isinstance(offset_trf, transforms.IdentityTransform) + or offset_trf.contains_branch(transData)): + # if the offsets are in some coords other than data, # then don't use them for autoscaling. return transforms.Bbox.null() offsets = self._offsets @@ -289,20 +289,19 @@ def get_datalim(self, transData): self.get_transforms(), offset_trf.transform_non_affine(offsets), offset_trf.get_affine().frozen()) - if has_offsets: - # this is for collections that have their paths (shapes) - # in physical, axes-relative, or figure-relative units - # (i.e. like scatter). We can't uniquely set limits based on - # those shapes, so we just set the limits based on their - # location. - - offsets = (offset_trf - transData).transform(offsets) - # note A-B means A B^{-1} - offsets = np.ma.masked_invalid(offsets) - if not offsets.mask.all(): - bbox = transforms.Bbox.null() - bbox.update_from_data_xy(offsets) - return bbox + + # this is for collections that have their paths (shapes) + # in physical, axes-relative, or figure-relative units + # (i.e. like scatter). We can't uniquely set limits based on + # those shapes, so we just set the limits based on their + # location. + offsets = (offset_trf - transData).transform(offsets) + # note A-B means A B^{-1} + offsets = np.ma.masked_invalid(offsets) + if not offsets.mask.all(): + bbox = transforms.Bbox.null() + bbox.update_from_data_xy(offsets) + return bbox return transforms.Bbox.null() def get_window_extent(self, renderer): diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 488d5a34fd81..acca60fd01c1 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -711,6 +711,22 @@ def test_singleton_autolim(): np.testing.assert_allclose(ax.get_xlim(), [-0.06, 0.06]) +@pytest.mark.parametrize("transform, expected", [ + ("transData", (-0.5, 3.5)), + ("transAxes", (2.8, 3.2)), +]) +def test_autolim_with_zeros(transform, expected): + # 1) Test that a scatter at (0, 0) data coordinates contributes to + # autoscaling even though any(offsets) would be False in that situation. + # 2) Test that specifying transAxes for the transform does not contribute + # to the autoscaling. + fig, ax = plt.subplots() + ax.scatter(0, 0, transform=getattr(ax, transform)) + ax.scatter(3, 3) + np.testing.assert_allclose(ax.get_ylim(), expected) + np.testing.assert_allclose(ax.get_xlim(), expected) + + @pytest.mark.parametrize('flat_ref, kwargs', [ (True, {}), (False, {}),