From 8357131482fcdfff307a9846128ebbb5e9df7c03 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 14 Aug 2015 01:51:15 -0400 Subject: [PATCH 1/2] ENH: Add TransformedPatchPath for clipping. By linking to the Patch instead of its Path, the clip path can automatically update whenever the underlying Patch changes. --- lib/matplotlib/artist.py | 9 ++++---- lib/matplotlib/transforms.py | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index a4614f214299..0f7478de0c66 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -12,7 +12,7 @@ from matplotlib.cbook import mplDeprecation from matplotlib import docstring, rcParams from .transforms import (Bbox, IdentityTransform, TransformedBbox, - TransformedPath, Transform) + TransformedPatchPath, TransformedPath, Transform) from .path import Path # Note, matplotlib artists use the doc strings for set and get @@ -685,9 +685,7 @@ def set_clip_path(self, path, transform=None): self._clippath = None success = True elif isinstance(path, Patch): - self._clippath = TransformedPath( - path.get_path(), - path.get_transform()) + self._clippath = TransformedPatchPath(path) success = True elif isinstance(path, tuple): path, transform = path @@ -698,6 +696,9 @@ def set_clip_path(self, path, transform=None): elif isinstance(path, Path): self._clippath = TransformedPath(path, transform) success = True + elif isinstance(path, TransformedPatchPath): + self._clippath = path + success = True elif isinstance(path, TransformedPath): self._clippath = path success = True diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 812047910e11..dd56352255b9 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -2715,6 +2715,46 @@ def get_affine(self): return self._transform.get_affine() +class TransformedPatchPath(TransformedPath): + """ + A :class:`TransformedPatchPath` caches a non-affine transformed copy of + the :class:`~matplotlib.path.Patch`. This cached copy is automatically + updated when the non-affine part of the transform or the patch changes. + """ + def __init__(self, patch): + """ + Create a new :class:`TransformedPatchPath` from the given + :class:`~matplotlib.path.Patch`. + """ + TransformNode.__init__(self) + + transform = patch.get_transform() + self._patch = patch + self._transform = transform + self.set_children(transform) + self._path = patch.get_path() + self._transformed_path = None + self._transformed_points = None + + def _revalidate(self): + patch_path = self._patch.get_path() + # Only recompute if the invalidation includes the non_affine part of + # the transform, or the Patch's Path has changed. + if (self._transformed_path is None or self._path != patch_path or + (self._invalid & self.INVALID_NON_AFFINE == + self.INVALID_NON_AFFINE)): + self._path = patch_path + self._transformed_path = \ + self._transform.transform_path_non_affine(patch_path) + self._transformed_points = \ + Path._fast_from_codes_and_verts( + self._transform.transform_non_affine(patch_path.vertices), + None, + {'interpolation_steps': patch_path._interpolation_steps, + 'should_simplify': patch_path.should_simplify}) + self._invalid = 0 + + def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): ''' Modify the endpoints of a range as needed to avoid singularities. From c2877b38f6aa6939fa48f506ed5b200ef20af0e1 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Mon, 19 Oct 2015 03:50:32 -0400 Subject: [PATCH 2/2] TST: Add tests for Transformed(Patch)Path. --- lib/matplotlib/tests/test_transforms.py | 44 ++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index 5190ddc873fe..d961741c06ac 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -10,7 +10,8 @@ import numpy.testing as np_test from numpy.testing import assert_almost_equal, assert_array_equal from numpy.testing import assert_array_almost_equal -from matplotlib.transforms import Affine2D, BlendedGenericTransform, Bbox +from matplotlib.transforms import (Affine2D, BlendedGenericTransform, Bbox, + TransformedPath, TransformedPatchPath) from matplotlib.path import Path from matplotlib.scale import LogScale from matplotlib.testing.decorators import cleanup, image_comparison @@ -576,6 +577,47 @@ def test_invalid_arguments(): assert_raises(RuntimeError, t.transform, [[1, 2, 3]]) +def test_transformed_path(): + points = [(0, 0), (1, 0), (1, 1), (0, 1)] + codes = [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY] + path = Path(points, codes) + + trans = mtrans.Affine2D() + trans_path = TransformedPath(path, trans) + assert np.allclose(trans_path.get_fully_transformed_path().vertices, + points) + + # Changing the transform should change the result. + r2 = 1 / np.sqrt(2) + trans.rotate(np.pi / 4) + assert np.allclose(trans_path.get_fully_transformed_path().vertices, + [(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)]) + + # Changing the path does not change the result (it's cached). + path.points = [(0, 0)] * 4 + assert np.allclose(trans_path.get_fully_transformed_path().vertices, + [(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)]) + + +def test_transformed_patch_path(): + trans = mtrans.Affine2D() + patch = mpatches.Wedge((0, 0), 1, 45, 135, transform=trans) + + tpatch = TransformedPatchPath(patch) + points = tpatch.get_fully_transformed_path().vertices + + # Changing the transform should change the result. + trans.scale(2) + assert np.allclose(tpatch.get_fully_transformed_path().vertices, + points * 2) + + # Changing the path should change the result (and cancel out the scaling + # from the transform). + patch.set_radius(0.5) + assert np.allclose(tpatch.get_fully_transformed_path().vertices, + points) + + if __name__ == '__main__': import nose nose.runmodule(argv=['-s', '--with-doctest'], exit=False) 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