diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index a9a92d33cff3..4145ff37f6f0 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -627,9 +627,9 @@ def test_invalid_arguments(): t.transform([]) with pytest.raises(RuntimeError): t.transform([1]) - with pytest.raises(RuntimeError): + with pytest.raises(ValueError): t.transform([[1]]) - with pytest.raises(RuntimeError): + with pytest.raises(ValueError): t.transform([[1, 2, 3]]) diff --git a/src/_backend_agg.h b/src/_backend_agg.h index dd73aecde0f2..2aafd8526171 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -731,22 +731,22 @@ inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, in rendererBase.reset_clipping(true); if (angle != 0.0) { agg::rendering_buffer srcbuf( - image.data(), (unsigned)image.dim(1), - (unsigned)image.dim(0), (unsigned)image.dim(1)); + image.data(), (unsigned)image.shape(1), + (unsigned)image.shape(0), (unsigned)image.shape(1)); agg::pixfmt_gray8 pixf_img(srcbuf); set_clipbox(gc.cliprect, theRasterizer); agg::trans_affine mtx; - mtx *= agg::trans_affine_translation(0, -image.dim(0)); + mtx *= agg::trans_affine_translation(0, -image.shape(0)); mtx *= agg::trans_affine_rotation(-angle * (agg::pi / 180.0)); mtx *= agg::trans_affine_translation(x, y); agg::path_storage rect; rect.move_to(0, 0); - rect.line_to(image.dim(1), 0); - rect.line_to(image.dim(1), image.dim(0)); - rect.line_to(0, image.dim(0)); + rect.line_to(image.shape(1), 0); + rect.line_to(image.shape(1), image.shape(0)); + rect.line_to(0, image.shape(0)); rect.line_to(0, 0); agg::conv_transform rect2(rect, mtx); @@ -767,10 +767,10 @@ inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, in } else { agg::rect_i fig, text; - int deltay = y - image.dim(0); + int deltay = y - image.shape(0); fig.init(0, 0, width, height); - text.init(x, deltay, x + image.dim(1), y); + text.init(x, deltay, x + image.shape(1), y); text.clip(fig); if (gc.cliprect.x1 != 0.0 || gc.cliprect.y1 != 0.0 || gc.cliprect.x2 != 0.0 || gc.cliprect.y2 != 0.0) { @@ -832,19 +832,19 @@ inline void RendererAgg::draw_image(GCAgg &gc, agg::rendering_buffer buffer; buffer.attach( - image.data(), (unsigned)image.dim(1), (unsigned)image.dim(0), -(int)image.dim(1) * 4); + image.data(), (unsigned)image.shape(1), (unsigned)image.shape(0), -(int)image.shape(1) * 4); pixfmt pixf(buffer); if (has_clippath) { agg::trans_affine mtx; agg::path_storage rect; - mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image.dim(0)))); + mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image.shape(0)))); rect.move_to(0, 0); - rect.line_to(image.dim(1), 0); - rect.line_to(image.dim(1), image.dim(0)); - rect.line_to(0, image.dim(0)); + rect.line_to(image.shape(1), 0); + rect.line_to(image.shape(1), image.shape(0)); + rect.line_to(0, image.shape(0)); rect.line_to(0, 0); agg::conv_transform rect2(rect, mtx); @@ -880,7 +880,7 @@ inline void RendererAgg::draw_image(GCAgg &gc, } else { set_clipbox(gc.cliprect, rendererBase); rendererBase.blend_from( - pixf, 0, (int)x, (int)(height - (y + image.dim(0))), (agg::int8u)(alpha * 255)); + pixf, 0, (int)x, (int)(height - (y + image.shape(0))), (agg::int8u)(alpha * 255)); } rendererBase.reset_clipping(true); @@ -918,15 +918,15 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, typedef agg::conv_curve curve_t; size_t Npaths = path_generator.num_paths(); - size_t Noffsets = offsets.size(); + size_t Noffsets = safe_first_shape(offsets); size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = transforms.size(); - size_t Nfacecolors = facecolors.size(); - size_t Nedgecolors = edgecolors.size(); - size_t Nlinewidths = linewidths.size(); + size_t Ntransforms = safe_first_shape(transforms); + size_t Nfacecolors = safe_first_shape(facecolors); + size_t Nedgecolors = safe_first_shape(edgecolors); + size_t Nlinewidths = safe_first_shape(linewidths); size_t Nlinestyles = std::min(linestyles.size(), N); - size_t Naa = antialiaseds.size(); + size_t Naa = safe_first_shape(antialiaseds); if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0) { return; @@ -1234,7 +1234,7 @@ inline void RendererAgg::draw_gouraud_triangles(GCAgg &gc, set_clipbox(gc.cliprect, theRasterizer); bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); - for (int i = 0; i < points.dim(0); ++i) { + for (int i = 0; i < points.shape(0); ++i) { typename PointArray::sub_t point = points.subarray(i); typename ColorArray::sub_t color = colors.subarray(i); diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index 02832f1c3e69..a8c40969e427 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -456,17 +456,17 @@ PyRendererAgg_draw_gouraud_triangles(PyRendererAgg *self, PyObject *args) &trans)) { return NULL; } - if (points.size() && !check_trailing_shape(points, "points", 3, 2)) { + if (points.shape(0) && !check_trailing_shape(points, "points", 3, 2)) { return NULL; } - if (colors.size() && !check_trailing_shape(colors, "colors", 3, 4)) { + if (colors.shape(0) && !check_trailing_shape(colors, "colors", 3, 4)) { return NULL; } - if (points.size() != colors.size()) { + if (points.shape(0) != colors.shape(0)) { PyErr_Format(PyExc_ValueError, "points and colors arrays must be the same length, got " "%" NPY_INTP_FMT " points and %" NPY_INTP_FMT "colors", - points.dim(0), colors.dim(0)); + points.shape(0), colors.shape(0)); return NULL; } diff --git a/src/_path.h b/src/_path.h index 387a0a6737f4..75ac1779b091 100644 --- a/src/_path.h +++ b/src/_path.h @@ -113,7 +113,7 @@ void point_in_path_impl(PointArray &points, PathIterator &path, ResultArray &ins size_t i; bool all_done; - size_t n = points.size(); + size_t n = safe_first_shape(points); std::vector yflag0(n); std::vector subpath_flag(n); @@ -247,7 +247,7 @@ inline void points_in_path(PointArray &points, typedef agg::conv_contour contour_t; size_t i; - for (i = 0; i < points.size(); ++i) { + for (i = 0; i < safe_first_shape(points); ++i) { result[i] = false; } @@ -379,14 +379,14 @@ void get_path_collection_extents(agg::trans_affine &master_transform, agg::trans_affine &offset_trans, extent_limits &extent) { - if (offsets.size() != 0 && offsets.dim(1) != 2) { + if (offsets.size() != 0 && offsets.shape(1) != 2) { throw std::runtime_error("Offsets array must have shape (N, 2)"); } size_t Npaths = paths.size(); - size_t Noffsets = offsets.size(); + size_t Noffsets = safe_first_shape(offsets); size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(transforms.size(), N); + size_t Ntransforms = std::min(safe_first_shape(transforms), N); size_t i; agg::trans_affine trans; @@ -436,9 +436,9 @@ void point_in_path_collection(double x, return; } - size_t Noffsets = offsets.size(); + size_t Noffsets = safe_first_shape(offsets); size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(transforms.size(), N); + size_t Ntransforms = std::min(safe_first_shape(transforms), N); size_t i; agg::trans_affine trans; @@ -709,11 +709,11 @@ clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside, std::vecto template void affine_transform_2d(VerticesArray &vertices, agg::trans_affine &trans, ResultArray &result) { - if (vertices.size() != 0 && vertices.dim(1) != 2) { + if (vertices.size() != 0 && vertices.shape(1) != 2) { throw std::runtime_error("Invalid vertices array."); } - size_t n = vertices.size(); + size_t n = vertices.shape(0); double x; double y; double t0; @@ -739,7 +739,7 @@ void affine_transform_2d(VerticesArray &vertices, agg::trans_affine &trans, Resu template void affine_transform_1d(VerticesArray &vertices, agg::trans_affine &trans, ResultArray &result) { - if (vertices.dim(0) != 2) { + if (vertices.shape(0) != 2) { throw std::runtime_error("Invalid vertices array."); } @@ -776,7 +776,7 @@ int count_bboxes_overlapping_bbox(agg::rect_d &a, BBoxArray &bboxes) std::swap(a.y1, a.y2); } - size_t num_bboxes = bboxes.size(); + size_t num_bboxes = safe_first_shape(bboxes); for (size_t i = 0; i < num_bboxes; ++i) { b = agg::rect_d(bboxes(i, 0, 0), bboxes(i, 0, 1), bboxes(i, 1, 0), bboxes(i, 1, 1)); diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp index 369d9e030880..cc6d1c05ebca 100644 --- a/src/_path_wrapper.cpp +++ b/src/_path_wrapper.cpp @@ -83,7 +83,11 @@ static PyObject *Py_points_in_path(PyObject *self, PyObject *args) return NULL; } - npy_intp dims[] = { (npy_intp)points.size() }; + if (!check_trailing_shape(points, "points", 2)) { + return NULL; + } + + npy_intp dims[] = { (npy_intp)points.shape(0) }; numpy::array_view results(dims); CALL_CPP("points_in_path", (points_in_path(points, r, path, trans, results))); @@ -118,10 +122,10 @@ static PyObject *Py_update_path_extents(PyObject *self, PyObject *args) return NULL; } - if (minpos.dim(0) != 2) { + if (minpos.shape(0) != 2) { PyErr_Format(PyExc_ValueError, "minpos must be of length 2, got %" NPY_INTP_FMT, - minpos.dim(0)); + minpos.shape(0)); return NULL; } @@ -361,7 +365,11 @@ static PyObject *Py_affine_transform(PyObject *self, PyObject *args) numpy::array_view vertices(vertices_arr); Py_DECREF(vertices_arr); - npy_intp dims[] = { (npy_intp)vertices.size(), 2 }; + if(!check_trailing_shape(vertices, "vertices", 2)) { + return NULL; + } + + npy_intp dims[] = { (npy_intp)vertices.shape(0), 2 }; numpy::array_view result(dims); CALL_CPP("affine_transform", (affine_transform_2d(vertices, trans, result))); return result.pyobj(); @@ -369,7 +377,7 @@ static PyObject *Py_affine_transform(PyObject *self, PyObject *args) numpy::array_view vertices(vertices_arr); Py_DECREF(vertices_arr); - npy_intp dims[] = { (npy_intp)vertices.size() }; + npy_intp dims[] = { (npy_intp)vertices.shape(0) }; numpy::array_view result(dims); CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result))); return result.pyobj(); diff --git a/src/_qhull_wrapper.cpp b/src/_qhull_wrapper.cpp index ef374d8e42c0..3abce833fe6f 100644 --- a/src/_qhull_wrapper.cpp +++ b/src/_qhull_wrapper.cpp @@ -267,8 +267,8 @@ delaunay(PyObject *self, PyObject *args) return NULL; } - npoints = xarray.dim(0); - if (npoints != yarray.dim(0)) { + npoints = xarray.shape(0); + if (npoints != yarray.shape(0)) { PyErr_SetString(PyExc_ValueError, "x and y must be 1D arrays of the same length"); return NULL; diff --git a/src/array.h b/src/array.h index 47d82995541b..6694412b8df5 100644 --- a/src/array.h +++ b/src/array.h @@ -29,7 +29,7 @@ class scalar return m_value; } - int dim(size_t i) + int shape(size_t i) { return 1; } @@ -40,6 +40,13 @@ class scalar } }; +template +size_t +safe_first_shape(scalar) +{ + return 1; +} + template class empty { @@ -65,7 +72,7 @@ class empty return empty(); } - int dim(size_t i) const + int shape(size_t i) const { return 0; } @@ -75,6 +82,12 @@ class empty return 0; } }; + +template +size_t safe_first_shape(empty) +{ + return 0; +} } #endif diff --git a/src/mplutils.h b/src/mplutils.h index 186449e8f63c..8c2921a1d2ff 100644 --- a/src/mplutils.h +++ b/src/mplutils.h @@ -72,10 +72,10 @@ inline int prepare_and_add_type(PyTypeObject *type, PyObject *module) template inline bool check_trailing_shape(T array, char const* name, long d1) { - if (array.dim(1) != d1) { + if (array.shape(1) != d1) { PyErr_Format(PyExc_ValueError, "%s must have shape (N, %ld), got (%ld, %ld)", - name, d1, array.dim(0), array.dim(1)); + name, d1, array.shape(0), array.shape(1)); return false; } return true; @@ -84,10 +84,10 @@ inline bool check_trailing_shape(T array, char const* name, long d1) template inline bool check_trailing_shape(T array, char const* name, long d1, long d2) { - if (array.dim(1) != d1 || array.dim(2) != d2) { + if (array.shape(1) != d1 || array.shape(2) != d2) { PyErr_Format(PyExc_ValueError, "%s must have shape (N, %ld, %ld), got (%ld, %ld, %ld)", - name, d1, d2, array.dim(0), array.dim(1), array.dim(2)); + name, d1, d2, array.shape(0), array.shape(1), array.shape(2)); return false; } return true; diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h index 36c763d1584e..b26f73e2c4fb 100644 --- a/src/numpy_cpp.h +++ b/src/numpy_cpp.h @@ -492,7 +492,7 @@ class array_view : public detail::array_view_accessors return true; } - npy_intp dim(size_t i) const + npy_intp shape(size_t i) const { if (i >= ND) { return 0; @@ -500,29 +500,7 @@ class array_view : public detail::array_view_accessors return m_shape[i]; } - /* - In most cases, code should use size() instead of dim(0), since - size() == 0 when any dimension is 0. - */ - size_t size() const - { - bool empty = (ND == 0); - for (size_t i = 0; i < ND; i++) { - if (m_shape[i] == 0) { - empty = true; - } - } - if (empty) { - return 0; - } else { - return (size_t)dim(0); - } - } - - bool empty() const - { - return size() == 0; - } + size_t size() const; // Do not use this for array_view. See comment near top of file. const T *data() const @@ -572,6 +550,32 @@ class array_view : public detail::array_view_accessors } }; +/* In most cases, code should use safe_first_shape(obj) instead of obj.shape(0), since + safe_first_shape(obj) == 0 when any dimension is 0. */ +template +size_t +safe_first_shape(const array_view &a) +{ + bool empty = (ND == 0); + for (size_t i = 0; i < ND; i++) { + if (a.shape(i) == 0) { + empty = true; + } + } + if (empty) { + return 0; + } else { + return (size_t)a.shape(0); + } +} + +template +size_t +array_view::size() const +{ + return safe_first_shape(*this); +} + } // namespace numpy