diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index dc48215dee43..58e4d955eeb8 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -80,9 +80,13 @@ class Collection(artist.Artist, cm.ScalarMappable): # _offsets must be a Nx2 array! _offsets.shape = (0, 2) _transOffset = transforms.IdentityTransform() - _transforms = [] - - + #: Either a list of 3x3 arrays or an Nx3x3 array of transforms, suitable + #: for the `all_transforms` argument to + #: :meth:`~matplotlib.backend_bases.RendererBase.draw_path_collection`; + #: each 3x3 array is used to initialize an + #: :class:`~matplotlib.transforms.Affine2D` object. + #: Each kind of collection defines this based on its arguments. + _transforms = np.empty((0, 3, 3)) def __init__(self, edgecolors=None, @@ -1515,7 +1519,7 @@ def __init__(self, widths, heights, angles, units='points', **kwargs): self._angles = np.asarray(angles).ravel() * (np.pi / 180.0) self._units = units self.set_transform(transforms.IdentityTransform()) - self._transforms = [] + self._transforms = np.empty((0, 3, 3)) self._paths = [mpath.Path.unit_circle()] def _set_transforms(self): diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 3c5a291edf1c..2e0d86456167 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -988,7 +988,8 @@ def get_path_collection_extents( if len(paths) == 0: raise ValueError("No paths provided") return Bbox.from_extents(*_path.get_path_collection_extents( - master_transform, paths, transforms, offsets, offset_transform)) + master_transform, paths, np.atleast_3d(transforms), + offsets, offset_transform)) def get_paths_extents(paths, transforms=[]): diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index 6d953de030ec..5190ddc873fe 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -561,6 +561,21 @@ def test_nonsingular(): assert_array_equal(out, zero_expansion) +def test_invalid_arguments(): + t = mtrans.Affine2D() + # There are two different exceptions, since the wrong number of + # dimensions is caught when constructing an array_view, and that + # raises a ValueError, and a wrong shape with a possible number + # of dimensions is caught by our CALL_CPP macro, which always + # raises the less precise RuntimeError. + assert_raises(ValueError, t.transform, 1) + assert_raises(ValueError, t.transform, [[[1]]]) + assert_raises(RuntimeError, t.transform, []) + assert_raises(RuntimeError, t.transform, [1]) + assert_raises(RuntimeError, t.transform, [[1]]) + assert_raises(RuntimeError, t.transform, [[1, 2, 3]]) + + if __name__ == '__main__': import nose nose.runmodule(argv=['-s', '--with-doctest'], exit=False) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 0df6ea4c4b12..54981f7f01ba 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -666,7 +666,8 @@ def count_overlaps(self, bboxes): bboxes is a sequence of :class:`BboxBase` objects """ - return count_bboxes_overlapping_bbox(self, [np.array(x) for x in bboxes]) + return count_bboxes_overlapping_bbox( + self, np.atleast_3d([np.array(x) for x in bboxes])) def expanded(self, sw, sh): """ diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp index a62893077c86..cd80e636dc3d 100644 --- a/src/_path_wrapper.cpp +++ b/src/_path_wrapper.cpp @@ -439,11 +439,16 @@ static PyObject *Py_affine_transform(PyObject *self, PyObject *args, PyObject *k CALL_CPP("affine_transform", (affine_transform_2d(vertices, trans, result))); return result.pyobj(); } catch (py::exception) { - numpy::array_view vertices(vertices_obj); - npy_intp dims[] = { vertices.dim(0) }; - numpy::array_view result(dims); - CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result))); - return result.pyobj(); + PyErr_Clear(); + try { + numpy::array_view vertices(vertices_obj); + npy_intp dims[] = { vertices.dim(0) }; + numpy::array_view result(dims); + CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result))); + return result.pyobj(); + } catch (py::exception) { + return NULL; + } } } diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h index 10ae68c328a5..c7cb14a74804 100644 --- a/src/numpy_cpp.h +++ b/src/numpy_cpp.h @@ -443,13 +443,18 @@ class array_view : public detail::array_view_accessors m_data = NULL; m_shape = zeros; m_strides = zeros; - } else if (PyArray_NDIM(tmp) != ND) { - PyErr_Format(PyExc_ValueError, - "Expected %d-dimensional array, got %d", - ND, - PyArray_NDIM(tmp)); - Py_DECREF(tmp); - return 0; + if (PyArray_NDIM(tmp) == 0 && ND == 0) { + m_arr = tmp; + return 1; + } + } + if (PyArray_NDIM(tmp) != ND) { + PyErr_Format(PyExc_ValueError, + "Expected %d-dimensional array, got %d", + ND, + PyArray_NDIM(tmp)); + Py_DECREF(tmp); + return 0; } /* Copy some of the data to the view object for faster access */