From cec26ca9faf31ba93ad639db73ae54c51edf1409 Mon Sep 17 00:00:00 2001 From: Cimarron Mittelsteadt Date: Tue, 13 Oct 2015 17:44:55 -0700 Subject: [PATCH 1/2] Revert "Use atleast_3d instead of ensure_3d" This reverts commit 2f5a633b1ac469f9c958a065896d832d94db0883. numpy.atleast_3d does not convert an empty array properly. --- lib/matplotlib/cbook.py | 15 +++++++++++++++ lib/matplotlib/path.py | 4 ++-- lib/matplotlib/tests/test_cbook.py | 8 ++++++++ lib/matplotlib/transforms.py | 3 ++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 18ef6b793c2b..c56956c04868 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2241,6 +2241,21 @@ def _reshape_2D(X): return X +def ensure_3d(arr): + """ + Return a version of arr with ndim==3, with extra dimensions added + at the end of arr.shape as needed. + """ + arr = np.asanyarray(arr) + if arr.ndim == 1: + arr = arr[:, None, None] + elif arr.ndim == 2: + arr = arr[:, :, None] + elif arr.ndim > 3 or arr.ndim < 1: + raise ValueError("cannot convert arr to 3-dimensional") + return arr + + def violin_stats(X, method, points=100): ''' Returns a list of dictionaries of data which can be used to draw a series diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 2e0d86456167..ca80f4cbb6b7 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -24,7 +24,7 @@ from numpy import ma from matplotlib import _path -from matplotlib.cbook import simple_linear_interpolation, maxdict +from matplotlib.cbook import simple_linear_interpolation, maxdict, ensure_3d from matplotlib import rcParams @@ -988,7 +988,7 @@ 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, np.atleast_3d(transforms), + master_transform, paths, ensure_3d(transforms), offsets, offset_transform)) diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 2b916b08566f..b244e761cf8f 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -376,3 +376,11 @@ def test_step_fails(): np.arange(12)) assert_raises(ValueError, cbook._step_validation, np.arange(12), np.arange(3)) + + +def test_ensure_3d(): + assert_array_equal([[[1]], [[2]], [[3]]], + cbook.ensure_3d([1, 2, 3])) + assert_array_equal([[[1], [2]], [[3], [4]]], + cbook.ensure_3d([[1, 2], [3, 4]])) + assert_raises(ValueError, cbook.ensure_3d, [[[[1]]]]) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 812047910e11..1eac27fc52ee 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -48,6 +48,7 @@ from sets import Set as set from .path import Path +from .cbook import ensure_3d DEBUG = False # we need this later, but this is very expensive to set up @@ -667,7 +668,7 @@ def count_overlaps(self, bboxes): bboxes is a sequence of :class:`BboxBase` objects """ return count_bboxes_overlapping_bbox( - self, np.atleast_3d([np.array(x) for x in bboxes])) + self, ensure_3d([np.array(x) for x in bboxes])) def expanded(self, sw, sh): """ From 44b536a8d148ae131a39fad34590a2ea976a76a4 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 14 Oct 2015 21:09:26 -0400 Subject: [PATCH 2/2] Fix #5185: Be more careful about array dims Check the dimensions on arrays passed to C++ to ensure they are what we expect. --- src/_backend_agg.h | 36 -------------- src/_backend_agg_wrapper.cpp | 50 ++++++++++++++++--- src/_path_wrapper.cpp | 19 ++++--- src/py_converters.cpp | 96 ++++++++++++++++++++++++++++++++++++ src/py_converters.h | 4 ++ 5 files changed, 154 insertions(+), 51 deletions(-) diff --git a/src/_backend_agg.h b/src/_backend_agg.h index ee85f00f7ad3..11abfcb98f5e 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -922,22 +922,6 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, typedef agg::conv_curve snapped_curve_t; typedef agg::conv_curve curve_t; - if (offsets.dim(0) != 0 && offsets.dim(1) != 2) { - throw "Offsets array must be Nx2 or empty"; - } - - if (facecolors.dim(0) != 0 && facecolors.dim(1) != 4) { - throw "Facecolors array must be a Nx4 array or empty"; - } - - if (edgecolors.dim(0) != 0 && edgecolors.dim(1) != 4) { - throw "Edgecolors array must by Nx4 or empty"; - } - - if (transforms.dim(0) != 0 && (transforms.dim(1) != 3 || transforms.dim(2) != 3)) { - throw "Transforms array must by Nx3x3 or empty"; - } - size_t Npaths = path_generator.num_paths(); size_t Noffsets = offsets.size(); size_t N = std::max(Npaths, Noffsets); @@ -1266,14 +1250,6 @@ inline void RendererAgg::draw_gouraud_triangle(GCAgg &gc, set_clipbox(gc.cliprect, theRasterizer); bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans); - if (points.dim(0) != 3 || points.dim(1) != 2) { - throw "points must be a 3x2 array"; - } - - if (colors.dim(0) != 3 || colors.dim(1) != 4) { - throw "colors must be a 3x4 array"; - } - _draw_gouraud_triangle(points, colors, trans, has_clippath); } @@ -1288,18 +1264,6 @@ inline void RendererAgg::draw_gouraud_triangles(GCAgg &gc, set_clipbox(gc.cliprect, theRasterizer); bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans); - if (points.dim(1) != 3 || points.dim(2) != 2) { - throw "points must be a Nx3x2 array"; - } - - if (colors.dim(1) != 3 || colors.dim(2) != 4) { - throw "colors must be a Nx3x4 array"; - } - - if (points.dim(0) != colors.dim(0)) { - throw "points and colors arrays must be the same length"; - } - for (int i = 0; i < points.dim(0); ++i) { typename PointArray::sub_t point = points[i]; typename ColorArray::sub_t color = colors[i]; diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index 753afe3837d2..752a8fc9484e 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -340,15 +340,15 @@ PyRendererAgg_draw_path_collection(PyRendererAgg *self, PyObject *args, PyObject &convert_trans_affine, &master_transform, &pathobj, - &transforms.converter, + &convert_transforms, &transforms, - &offsets.converter, + &convert_points, &offsets, &convert_trans_affine, &offset_trans, - &facecolors.converter, + &convert_colors, &facecolors, - &edgecolors.converter, + &convert_colors, &edgecolors, &linewidths.converter, &linewidths, @@ -411,14 +411,14 @@ static PyObject *PyRendererAgg_draw_quad_mesh(PyRendererAgg *self, PyObject *arg &mesh_height, &coordinates.converter, &coordinates, - &offsets.converter, + &convert_points, &offsets, &convert_trans_affine, &offset_trans, - &facecolors.converter, + &convert_colors, &facecolors, &antialiased, - &edgecolors.converter, + &convert_colors, &edgecolors)) { return NULL; } @@ -459,6 +459,21 @@ PyRendererAgg_draw_gouraud_triangle(PyRendererAgg *self, PyObject *args, PyObjec return NULL; } + if (points.dim(0) != 3 || points.dim(1) != 2) { + PyErr_Format(PyExc_ValueError, + "points must be a 3x2 array, got %dx%d", + points.dim(0), points.dim(1)); + return NULL; + } + + if (colors.dim(0) != 3 || colors.dim(1) != 4) { + PyErr_Format(PyExc_ValueError, + "colors must be a 3x4 array, got %dx%d", + colors.dim(0), colors.dim(1)); + return NULL; + } + + CALL_CPP("draw_gouraud_triangle", (self->x->draw_gouraud_triangle(gc, points, colors, trans))); Py_RETURN_NONE; @@ -485,6 +500,27 @@ PyRendererAgg_draw_gouraud_triangles(PyRendererAgg *self, PyObject *args, PyObje return NULL; } + if (points.dim(1) != 3 || points.dim(2) != 2) { + PyErr_Format(PyExc_ValueError, + "points must be a Nx3x2 array, got %dx%dx%d", + points.dim(0), points.dim(1), points.dim(2)); + return NULL; + } + + if (colors.dim(1) != 3 || colors.dim(2) != 4) { + PyErr_Format(PyExc_ValueError, + "colors must be a Nx3x4 array, got %dx%dx%d", + colors.dim(0), colors.dim(1), colors.dim(2)); + return NULL; + } + + if (points.dim(0) != colors.dim(0)) { + PyErr_Format(PyExc_ValueError, + "points and colors arrays must be the same length, got %d and %d", + points.dim(0), colors.dim(0)); + return NULL; + } + CALL_CPP("draw_gouraud_triangles", self->x->draw_gouraud_triangles(gc, points, colors, trans)); Py_RETURN_NONE; diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp index cd80e636dc3d..e824bf290d60 100644 --- a/src/_path_wrapper.cpp +++ b/src/_path_wrapper.cpp @@ -69,7 +69,7 @@ static PyObject *Py_points_in_path(PyObject *self, PyObject *args, PyObject *kwd if (!PyArg_ParseTuple(args, "O&dO&O&:points_in_path", - &points.converter, + &convert_points, &points, &r, &convert_path, @@ -128,7 +128,7 @@ static PyObject *Py_points_on_path(PyObject *self, PyObject *args, PyObject *kwd if (!PyArg_ParseTuple(args, "O&dO&O&:points_on_path", - &points.converter, + &convert_points, &points, &r, &convert_path, @@ -200,7 +200,10 @@ static PyObject *Py_update_path_extents(PyObject *self, PyObject *args, PyObject } if (minpos.dim(0) != 2) { - PyErr_SetString(PyExc_ValueError, "minpos must be of length 2"); + PyErr_Format(PyExc_ValueError, + "minpos must be of length 2, got %d", + minpos.dim(0)); + return NULL; } extent_limits e; @@ -263,9 +266,9 @@ static PyObject *Py_get_path_collection_extents(PyObject *self, PyObject *args, &convert_trans_affine, &master_transform, &pathsobj, - &transforms.converter, + &convert_transforms, &transforms, - &offsets.converter, + &convert_points, &offsets, &convert_trans_affine, &offset_trans)) { @@ -319,9 +322,9 @@ static PyObject *Py_point_in_path_collection(PyObject *self, PyObject *args, PyO &convert_trans_affine, &master_transform, &pathsobj, - &transforms.converter, + &convert_transforms, &transforms, - &offsets.converter, + &convert_points, &offsets, &convert_trans_affine, &offset_trans, @@ -464,7 +467,7 @@ static PyObject *Py_count_bboxes_overlapping_bbox(PyObject *self, PyObject *args "O&O&:count_bboxes_overlapping_bbox", &convert_rect, &bbox, - &bboxes.converter, + &convert_bboxes, &bboxes)) { return NULL; } diff --git a/src/py_converters.cpp b/src/py_converters.cpp index 8635d0271158..23365c71565e 100644 --- a/src/py_converters.cpp +++ b/src/py_converters.cpp @@ -518,4 +518,100 @@ int convert_face(PyObject *color, GCAgg &gc, agg::rgba *rgba) return 1; } + +int convert_points(PyObject *obj, void *pointsp) +{ + numpy::array_view *points = (numpy::array_view *)pointsp; + + if (obj == NULL || obj == Py_None) { + return 1; + } + + points->set(obj); + + if (points->dim(0) == 0) { + return 1; + } + + if (points->dim(1) != 2) { + PyErr_Format(PyExc_ValueError, + "Points must be Nx2 array, got %dx%d", + points->dim(0), points->dim(1)); + return 0; + } + + return 1; +} + +int convert_transforms(PyObject *obj, void *transp) +{ + numpy::array_view *trans = (numpy::array_view *)transp; + + if (obj == NULL || obj == Py_None) { + return 1; + } + + trans->set(obj); + + if (trans->dim(0) == 0) { + return 1; + } + + if (trans->dim(1) != 3 || trans->dim(2) != 3) { + PyErr_Format(PyExc_ValueError, + "Transforms must be Nx3x3 array, got %dx%dx%d", + trans->dim(0), trans->dim(1), trans->dim(2)); + return 0; + } + + return 1; +} + +int convert_bboxes(PyObject *obj, void *bboxp) +{ + numpy::array_view *bbox = (numpy::array_view *)bboxp; + + if (obj == NULL || obj == Py_None) { + return 1; + } + + bbox->set(obj); + + if (bbox->dim(0) == 0) { + return 1; + } + + if (bbox->dim(1) != 2 || bbox->dim(2) != 2) { + PyErr_Format(PyExc_ValueError, + "Bbox array must be Nx2x2 array, got %dx%dx%d", + bbox->dim(0), bbox->dim(1), bbox->dim(2)); + return 0; + } + + return 1; +} + +int convert_colors(PyObject *obj, void *colorsp) +{ + numpy::array_view *colors = (numpy::array_view *)colorsp; + + if (obj == NULL || obj == Py_None) { + return 1; + } + + colors->set(obj); + + if (colors->dim(0) == 0) { + return 1; + } + + if (colors->dim(1) != 4) { + PyErr_Format(PyExc_ValueError, + "Colors array must be Nx4 array, got %dx%d", + colors->dim(0), colors->dim(1)); + return 0; + } + + return 1; +} } diff --git a/src/py_converters.h b/src/py_converters.h index 90cf454e30fb..02d84affe857 100644 --- a/src/py_converters.h +++ b/src/py_converters.h @@ -38,6 +38,10 @@ int convert_snap(PyObject *obj, void *snapp); int convert_offset_position(PyObject *obj, void *offsetp); int convert_sketch_params(PyObject *obj, void *sketchp); int convert_gcagg(PyObject *pygc, void *gcp); +int convert_points(PyObject *pygc, void *pointsp); +int convert_transforms(PyObject *pygc, void *transp); +int convert_bboxes(PyObject *pygc, void *bboxp); +int convert_colors(PyObject *pygc, void *colorsp); int convert_face(PyObject *color, GCAgg &gc, agg::rgba *rgba); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy