From f884001578118f578b5c4c73f2349d906e7ee38c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 19 Jan 2015 20:21:03 -0500 Subject: [PATCH 1/2] BUG : provide get_window_extent for QuadMesh fixes #3095 Without a `get_window_extent` method defined, it was falling back to the `Collection` version which in turn called (eventually) `covert_mesh_to_paths`, which resulted in a >5x blow up in the memory usage. --- lib/matplotlib/collections.py | 36 ++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 3e781d3602e8..8086c5444d80 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -82,8 +82,6 @@ class Collection(artist.Artist, cm.ScalarMappable): _transOffset = transforms.IdentityTransform() _transforms = [] - - def __init__(self, edgecolors=None, facecolors=None, @@ -1501,7 +1499,7 @@ def _set_transforms(self): self._transforms[:, 1, 0] = widths * sin_angle self._transforms[:, 1, 1] = heights * cos_angle self._transforms[:, 2, 2] = 1.0 - + _affine = transforms.Affine2D if self._units == 'xy': m = ax.transData.get_affine().get_matrix().copy() @@ -1705,6 +1703,38 @@ def set_paths(self): self._paths = self.convert_mesh_to_paths( self._meshWidth, self._meshHeight, self._coordinates) + def get_window_extent(self, renderer): + # get a dummy bounding box + bbox = transforms.Bbox([[0, 0], [0, 0]]) + # get the data and offset transforms + trans_data_to_xy = self.get_transform().transform + transOffset = self.get_offset_transform().transform + # convert the transforms to data-units + offsets = transOffset(self._offsets) + + # deal with masked arrays + coordinates = self._coordinates + if ma.isMaskedArray(coordinates): + c = coordinates.data + else: + c = coordinates + # slice sets to pick out the corners of each patch + # bottom-left, top-left, top-right, bottom-right + slice_sets = ((slice(0, -1), slice(0, -1)), + (slice(0, -1), slice(1, None)), + (slice(1, None), slice(1, None)), + (slice(1, None), slice(0, -1)) + ) + # first time ignore the existing bounds + ignore = True + for x_slc, y_slc in slice_sets: + bbox.update_from_data_xy( + trans_data_to_xy(c[x_slc, y_slc].reshape(-1, 2) + offsets), + ignore=ignore) + # but on subsequent loops don't shrink the bounding box + ignore = False + return bbox + @staticmethod def convert_mesh_to_paths(meshWidth, meshHeight, coordinates): """ From 349d6da1acc47fc5167764c3c7645daa1e148173 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 19 Jan 2015 23:04:00 -0500 Subject: [PATCH 2/2] MNT : make sure the correct ignore behavior is used --- lib/matplotlib/collections.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 8086c5444d80..54e8563bb229 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -1687,7 +1687,8 @@ def __init__(self, meshWidth, meshHeight, coordinates, self._bbox = transforms.Bbox.unit() self._bbox.update_from_data_xy(coordinates.reshape( - ((meshWidth + 1) * (meshHeight + 1), 2))) + ((meshWidth + 1) * (meshHeight + 1), 2)), + ignore=True) # By converting to floats now, we can avoid that on every draw. self._coordinates = self._coordinates.reshape(