Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit de19d5c

Browse filesBrowse files
committed
Merge pull request #4920 from QuLogic/transformedpatch
ENH: Add TransformedPatchPath for clipping.
2 parents 54ead93 + c2877b3 commit de19d5c
Copy full SHA for de19d5c

File tree

Expand file treeCollapse file tree

3 files changed

+88
-5
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+88
-5
lines changed

‎lib/matplotlib/artist.py

Copy file name to clipboardExpand all lines: lib/matplotlib/artist.py
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from matplotlib.cbook import mplDeprecation
1313
from matplotlib import docstring, rcParams
1414
from .transforms import (Bbox, IdentityTransform, TransformedBbox,
15-
TransformedPath, Transform)
15+
TransformedPatchPath, TransformedPath, Transform)
1616
from .path import Path
1717

1818
# Note, matplotlib artists use the doc strings for set and get
@@ -685,9 +685,7 @@ def set_clip_path(self, path, transform=None):
685685
self._clippath = None
686686
success = True
687687
elif isinstance(path, Patch):
688-
self._clippath = TransformedPath(
689-
path.get_path(),
690-
path.get_transform())
688+
self._clippath = TransformedPatchPath(path)
691689
success = True
692690
elif isinstance(path, tuple):
693691
path, transform = path
@@ -698,6 +696,9 @@ def set_clip_path(self, path, transform=None):
698696
elif isinstance(path, Path):
699697
self._clippath = TransformedPath(path, transform)
700698
success = True
699+
elif isinstance(path, TransformedPatchPath):
700+
self._clippath = path
701+
success = True
701702
elif isinstance(path, TransformedPath):
702703
self._clippath = path
703704
success = True

‎lib/matplotlib/tests/test_transforms.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_transforms.py
+43-1Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
import numpy.testing as np_test
1111
from numpy.testing import assert_almost_equal, assert_array_equal
1212
from numpy.testing import assert_array_almost_equal
13-
from matplotlib.transforms import Affine2D, BlendedGenericTransform, Bbox
13+
from matplotlib.transforms import (Affine2D, BlendedGenericTransform, Bbox,
14+
TransformedPath, TransformedPatchPath)
1415
from matplotlib.path import Path
1516
from matplotlib.scale import LogScale
1617
from matplotlib.testing.decorators import cleanup, image_comparison
@@ -576,6 +577,47 @@ def test_invalid_arguments():
576577
assert_raises(RuntimeError, t.transform, [[1, 2, 3]])
577578

578579

580+
def test_transformed_path():
581+
points = [(0, 0), (1, 0), (1, 1), (0, 1)]
582+
codes = [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]
583+
path = Path(points, codes)
584+
585+
trans = mtrans.Affine2D()
586+
trans_path = TransformedPath(path, trans)
587+
assert np.allclose(trans_path.get_fully_transformed_path().vertices,
588+
points)
589+
590+
# Changing the transform should change the result.
591+
r2 = 1 / np.sqrt(2)
592+
trans.rotate(np.pi / 4)
593+
assert np.allclose(trans_path.get_fully_transformed_path().vertices,
594+
[(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)])
595+
596+
# Changing the path does not change the result (it's cached).
597+
path.points = [(0, 0)] * 4
598+
assert np.allclose(trans_path.get_fully_transformed_path().vertices,
599+
[(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)])
600+
601+
602+
def test_transformed_patch_path():
603+
trans = mtrans.Affine2D()
604+
patch = mpatches.Wedge((0, 0), 1, 45, 135, transform=trans)
605+
606+
tpatch = TransformedPatchPath(patch)
607+
points = tpatch.get_fully_transformed_path().vertices
608+
609+
# Changing the transform should change the result.
610+
trans.scale(2)
611+
assert np.allclose(tpatch.get_fully_transformed_path().vertices,
612+
points * 2)
613+
614+
# Changing the path should change the result (and cancel out the scaling
615+
# from the transform).
616+
patch.set_radius(0.5)
617+
assert np.allclose(tpatch.get_fully_transformed_path().vertices,
618+
points)
619+
620+
579621
if __name__ == '__main__':
580622
import nose
581623
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

‎lib/matplotlib/transforms.py

Copy file name to clipboardExpand all lines: lib/matplotlib/transforms.py
+40Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2715,6 +2715,46 @@ def get_affine(self):
27152715
return self._transform.get_affine()
27162716

27172717

2718+
class TransformedPatchPath(TransformedPath):
2719+
"""
2720+
A :class:`TransformedPatchPath` caches a non-affine transformed copy of
2721+
the :class:`~matplotlib.path.Patch`. This cached copy is automatically
2722+
updated when the non-affine part of the transform or the patch changes.
2723+
"""
2724+
def __init__(self, patch):
2725+
"""
2726+
Create a new :class:`TransformedPatchPath` from the given
2727+
:class:`~matplotlib.path.Patch`.
2728+
"""
2729+
TransformNode.__init__(self)
2730+
2731+
transform = patch.get_transform()
2732+
self._patch = patch
2733+
self._transform = transform
2734+
self.set_children(transform)
2735+
self._path = patch.get_path()
2736+
self._transformed_path = None
2737+
self._transformed_points = None
2738+
2739+
def _revalidate(self):
2740+
patch_path = self._patch.get_path()
2741+
# Only recompute if the invalidation includes the non_affine part of
2742+
# the transform, or the Patch's Path has changed.
2743+
if (self._transformed_path is None or self._path != patch_path or
2744+
(self._invalid & self.INVALID_NON_AFFINE ==
2745+
self.INVALID_NON_AFFINE)):
2746+
self._path = patch_path
2747+
self._transformed_path = \
2748+
self._transform.transform_path_non_affine(patch_path)
2749+
self._transformed_points = \
2750+
Path._fast_from_codes_and_verts(
2751+
self._transform.transform_non_affine(patch_path.vertices),
2752+
None,
2753+
{'interpolation_steps': patch_path._interpolation_steps,
2754+
'should_simplify': patch_path.should_simplify})
2755+
self._invalid = 0
2756+
2757+
27182758
def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True):
27192759
'''
27202760
Modify the endpoints of a range as needed to avoid singularities.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.