Skip to content

Commit 3b4a07a

Browse files
Support for recursive rendering,
and filtering.
1 parent 14f4404 commit 3b4a07a

File tree

2 files changed

+94
-17
lines changed

2 files changed

+94
-17
lines changed

matplotview/__init__.py

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
from matplotview._view_axes import view_wrapper
1+
from typing import Callable, Optional, Iterable
2+
from matplotlib.artist import Artist
3+
from matplotlib.axes import Axes
4+
from matplotview._view_axes import view_wrapper, DEFAULT_RENDER_DEPTH
25

3-
def view(axes, axes_to_view, image_interpolation="nearest"):
6+
def view(
7+
axes: Axes,
8+
axes_to_view: Axes,
9+
image_interpolation: str = "nearest",
10+
render_depth: int = DEFAULT_RENDER_DEPTH,
11+
filter_function: Optional[Callable[[Artist], bool]] = None
12+
) -> Axes:
413
"""
514
Convert an axes into a view of another axes, displaying the contents of
615
the second axes.
@@ -14,18 +23,41 @@ def view(axes, axes_to_view, image_interpolation="nearest"):
1423
The axes to display the contents of in the first axes, the 'viewed'
1524
axes.
1625
17-
image_interpolation:
26+
image_interpolation: string, default of "nearest"
1827
The image interpolation method to use when displaying scaled images
1928
from the axes being viewed. Defaults to "nearest". Supported options
2029
are 'antialiased', 'nearest', 'bilinear', 'bicubic', 'spline16',
2130
'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric',
2231
'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos',
2332
or 'none'
33+
34+
render_depth: int, positive, defaults to 10
35+
The number of recursive draws allowed for this view, this can happen
36+
if the view is a child of the axes (such as an inset axes) or if
37+
two views point at each other. Defaults to 10.
38+
39+
filter_function: callable(Artist) -> bool or None
40+
An optional filter function, which can be used to select what artists
41+
are drawn by the view. If the function returns True, the element is
42+
drawn, otherwise it isn't. Defaults to None, or drawing all artists.
2443
"""
25-
return view_wrapper(type(axes)).from_axes(axes, axes_to_view, image_interpolation)
44+
return view_wrapper(type(axes)).from_axes(
45+
axes, axes_to_view, image_interpolation,
46+
render_depth, filter_function
47+
)
2648

2749

28-
def inset_zoom_axes(axes, bounds, *, image_interpolation="nearest", transform=None, zorder=5, **kwargs):
50+
def inset_zoom_axes(
51+
axes: Axes,
52+
bounds: Iterable,
53+
*,
54+
image_interpolation="nearest",
55+
render_depth: int = DEFAULT_RENDER_DEPTH,
56+
filter_function: Optional[Callable[[Artist], bool]] = None,
57+
transform=None,
58+
zorder=5,
59+
**kwargs
60+
) -> Axes:
2961
"""
3062
Add a child inset Axes to an Axes, which automatically plots
3163
artists contained within the parent Axes.
@@ -55,6 +87,16 @@ def inset_zoom_axes(axes, bounds, *, image_interpolation="nearest", transform=No
5587
determines the interpolation used when attempting to render a
5688
zoomed version of an image.
5789
90+
render_depth: int, positive, defaults to 10
91+
The number of recursive draws allowed for this view, this can happen
92+
if the view is a child of the axes (such as an inset axes) or if
93+
two views point at each other. Defaults to 10.
94+
95+
filter_function: callable(Artist) -> bool or None
96+
An optional filter function, which can be used to select what artists
97+
are drawn by the view. If the function returns True, the element is
98+
drawn, otherwise it isn't. Defaults to None, or drawing all artists.
99+
58100
**kwargs
59101
Other keyword arguments are passed on to the child `.Axes`.
60102
@@ -70,4 +112,7 @@ def inset_zoom_axes(axes, bounds, *, image_interpolation="nearest", transform=No
70112
inset_ax = axes.inset_axes(
71113
bounds, transform=transform, zorder=zorder, **kwargs
72114
)
73-
return view(inset_ax, axes, image_interpolation)
115+
return view(
116+
inset_ax, axes, image_interpolation,
117+
render_depth, filter_function
118+
)

matplotview/_view_axes.py

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import itertools
2-
from typing import Type, List
2+
from typing import Type, List, Optional, Callable
33
from matplotlib.axes import Axes
44
from matplotlib.transforms import Bbox
55
import matplotlib.docstring as docstring
66
from matplotview._transform_renderer import _TransformRenderer
77
from matplotlib.artist import Artist
88
from matplotlib.backend_bases import RendererBase
99

10+
DEFAULT_RENDER_DEPTH = 10
11+
1012
class BoundRendererArtist:
1113
def __init__(self, artist: Artist, renderer: RendererBase, clip_box: Bbox):
1214
self._artist = artist
@@ -66,17 +68,17 @@ class ViewAxesImpl(axes_class):
6668
require Artists to be plotted twice.
6769
"""
6870
__module__ = axes_class.__module__
69-
# The number of allowed recursions in the draw method
70-
MAX_RENDER_DEPTH = 5
7171

7272
def __init__(
7373
self,
7474
axes_to_view: Axes,
7575
*args,
7676
image_interpolation: str = "nearest",
77+
render_depth: int = DEFAULT_RENDER_DEPTH,
78+
filter_function: Optional[Callable[[Artist], bool]] = None,
7779
**kwargs
7880
):
79-
"""
81+
f"""
8082
Construct a new view axes.
8183
8284
Parameters
@@ -96,6 +98,17 @@ def __init__(
9698
'nearest'. This determines the interpolation used when
9799
attempting to render a view of an image.
98100
101+
render_depth: int, positive, defaults to 10
102+
The number of recursive draws allowed for this view, this can
103+
happen if the view is a child of the axes (such as an inset
104+
axes) or if two views point at each other. Defaults to 10.
105+
106+
filter_function: callable(Artist) -> bool or None
107+
An optional filter function, which can be used to select what
108+
artists are drawn by the view. If the function returns True,
109+
the element is drawn, otherwise it isn't. Defaults to None,
110+
or drawing all artists.
111+
99112
**kwargs
100113
Other optional keyword arguments supported by the Axes
101114
constructor this ViewAxes wraps:
@@ -108,15 +121,29 @@ def __init__(
108121
The new zoom view axes instance...
109122
"""
110123
super().__init__(axes_to_view.figure, *args, **kwargs)
111-
self._init_vars(axes_to_view, image_interpolation)
124+
self._init_vars(
125+
axes_to_view, image_interpolation,
126+
render_depth, filter_function
127+
)
112128

113129
def _init_vars(
114130
self,
115131
axes_to_view: Axes,
116-
image_interpolation: str = "nearest"
132+
image_interpolation: str,
133+
render_depth: int,
134+
filter_function: Optional[Callable[[Artist], bool]]
117135
):
136+
if(render_depth < 1):
137+
raise ValueError(f"Render depth of {render_depth} is invalid.")
138+
if(filter_function is None):
139+
filter_function = lambda a: True
140+
if(not callable(filter_function)):
141+
raise ValueError(f"The filter function must be a callable!")
142+
118143
self.__view_axes = axes_to_view
119144
self.__image_interpolation = image_interpolation
145+
self.__max_render_depth = render_depth
146+
self.__filter_function = filter_function
120147
self._render_depth = 0
121148
self.__scale_lines = True
122149
self.__renderer = None
@@ -144,7 +171,7 @@ def get_children(self) -> List[Artist]:
144171
BoundRendererArtist(a, mock_renderer, axes_box)
145172
for a in itertools.chain(
146173
self.__view_axes._children, self.__view_axes.child_axes
147-
) if(a is not self)
174+
) if(self.__filter_function(a))
148175
])
149176

150177
return init_list
@@ -155,7 +182,7 @@ def draw(self, renderer: RendererBase = None):
155182
# It is possible to have two axes which are views of each other
156183
# therefore we track the number of recursions and stop drawing
157184
# at a certain depth
158-
if(self._render_depth >= self.MAX_RENDER_DEPTH):
185+
if(self._render_depth >= self.__max_render_depth):
159186
return
160187
self._render_depth += 1
161188
# Set the renderer, causing get_children to return the view's
@@ -197,10 +224,15 @@ def from_axes(
197224
cls,
198225
axes: Axes,
199226
axes_to_view: Axes,
200-
image_interpolation: str = "nearest"
201-
):
227+
image_interpolation: str = "nearest",
228+
render_depth: int = DEFAULT_RENDER_DEPTH,
229+
filter_function: Optional[Callable[[Artist], bool]] = None
230+
) -> Axes:
202231
axes.__class__ = cls
203-
axes._init_vars(axes_to_view, image_interpolation)
232+
axes._init_vars(
233+
axes_to_view, image_interpolation,
234+
render_depth, filter_function
235+
)
204236
return axes
205237

206238
new_name = f"{ViewAxesImpl.__name__}[{axes_class.__name__}]"

0 commit comments

Comments
 (0)