From 1c5ee87972d859c6837865267ed09aec29a46c14 Mon Sep 17 00:00:00 2001 From: Matt Li Date: Sat, 29 Nov 2014 16:34:39 +0000 Subject: [PATCH 1/6] Ensure that IdentityTransform always returns an array. --- lib/matplotlib/transforms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index c8848eca900f..b553a66bc21a 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1944,7 +1944,7 @@ def get_matrix(self): get_matrix.__doc__ = Affine2DBase.get_matrix.__doc__ def transform(self, points): - return points + return np.asanyarray(points) transform.__doc__ = Affine2DBase.transform.__doc__ transform_affine = transform From b5685f4dff571482f5dd5f9143da31229edb4cce Mon Sep 17 00:00:00 2001 From: Matt Li Date: Sat, 29 Nov 2014 16:36:57 +0000 Subject: [PATCH 2/6] Ensure that 'points' is an array to avoid errors when indexing (completes fix for #3809). --- lib/matplotlib/transforms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index b553a66bc21a..a144b7d9e09c 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -2040,6 +2040,8 @@ def __repr__(self): return "BlendedGenericTransform(%s,%s)" % (self._x, self._y) def transform_non_affine(self, points): + points = np.asanyarray(points) + if self._x.is_affine and self._y.is_affine: return points x = self._x From 9dcc1fa5c63083f4b63275ab6b7cd329b70d3413 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Thu, 4 Dec 2014 10:14:11 +0000 Subject: [PATCH 3/6] Even with the fix for single-point transforms (77ccc8230) it's better to ensure we have a 2D array to avoid further case distinctions below. --- lib/matplotlib/transforms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index a144b7d9e09c..a0d439a57ecb 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -2040,7 +2040,7 @@ def __repr__(self): return "BlendedGenericTransform(%s,%s)" % (self._x, self._y) def transform_non_affine(self, points): - points = np.asanyarray(points) + points = np.asanyarray(points).reshape((-1, 2)) if self._x.is_affine and self._y.is_affine: return points From 49d2cf110e80f43ce3bb4c9d46fce6761f0b0216 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Thu, 4 Dec 2014 10:17:46 +0000 Subject: [PATCH 4/6] Add test for log transform. --- lib/matplotlib/tests/test_transforms.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index f6534513c4bd..b0bce5119c1f 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -487,6 +487,15 @@ def test_bbox_as_strings(): assert_equal(eval(format(getattr(b, k), fmt)), v) +@cleanup +def test_log_transform(): + # Tests that the last line runs without exception (previously the + # transform would fail if one of the axes was logarithmic). + fig, ax = plt.subplots() + ax.set_yscale('log') + ax.transData.transform((1,1)) + + if __name__=='__main__': import nose nose.runmodule(argv=['-s','--with-doctest'], exit=False) From 7d35c5edfda464bc6944d355bcf943edce994089 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Thu, 4 Dec 2014 11:34:56 +0000 Subject: [PATCH 5/6] Since most transformation code assumes 2d input, convert the values to a 2d array before applying any transforms and then convert the result back to the original shape. --- lib/matplotlib/transforms.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index a0d439a57ecb..56be99cfdab7 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1285,8 +1285,30 @@ def transform(self, values): Accepts a numpy array of shape (N x :attr:`input_dims`) and returns a numpy array of shape (N x :attr:`output_dims`). + + Alternatively, accepts a numpy array of length :attr:`input_dims` + and returns a numpy array of length :attr:`output_dims`. """ - return self.transform_affine(self.transform_non_affine(values)) + # Ensure that values is a 2d array (but remember whether + # we started with a 1d or 2d array). + values = np.asanyarray(values) + ndim = values.ndim + values = values.reshape((-1, self.input_dims)) + + # Transform the values + res = self.transform_affine(self.transform_non_affine(values)) + + # Convert the result back to the shape of the input values. + if ndim == 1: + return res.reshape(-1) + elif ndim == 2: + return res + else: + raise ValueError( + "Input values must have shape (N x {dims}) " + "or ({dims}).".format(dims=self.input_dims)) + + return res def transform_affine(self, values): """ @@ -1302,6 +1324,9 @@ def transform_affine(self, values): Accepts a numpy array of shape (N x :attr:`input_dims`) and returns a numpy array of shape (N x :attr:`output_dims`). + + Alternatively, accepts a numpy array of length :attr:`input_dims` + and returns a numpy array of length :attr:`output_dims`. """ return self.get_affine().transform(values) @@ -1318,6 +1343,9 @@ def transform_non_affine(self, values): Accepts a numpy array of shape (N x :attr:`input_dims`) and returns a numpy array of shape (N x :attr:`output_dims`). + + Alternatively, accepts a numpy array of length :attr:`input_dims` + and returns a numpy array of length :attr:`output_dims`. """ return values @@ -2040,8 +2068,6 @@ def __repr__(self): return "BlendedGenericTransform(%s,%s)" % (self._x, self._y) def transform_non_affine(self, points): - points = np.asanyarray(points).reshape((-1, 2)) - if self._x.is_affine and self._y.is_affine: return points x = self._x From 0b43f8397ec2b10a23e835209c1a61570819dbec Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Thu, 4 Dec 2014 11:58:56 +0000 Subject: [PATCH 6/6] Support transforming scalars as some routines need this. --- lib/matplotlib/transforms.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 56be99cfdab7..0c7a48cbf7c7 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1299,6 +1299,9 @@ def transform(self, values): res = self.transform_affine(self.transform_non_affine(values)) # Convert the result back to the shape of the input values. + if ndim == 0: + assert not np.ma.is_masked(res) # just to be on the safe side + return res[0, 0] if ndim == 1: return res.reshape(-1) elif ndim == 2: