Skip to content

Commit 19dc1b0

Browse files
Better handling of clipping, fixes for polar
projection support.
1 parent 6be6808 commit 19dc1b0

File tree

3 files changed

+57
-20
lines changed

3 files changed

+57
-20
lines changed

matplotview/_transform_renderer.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from matplotlib.backend_bases import RendererBase
2-
from matplotlib.transforms import Bbox, IdentityTransform, Affine2D
2+
from matplotlib.patches import Rectangle
3+
from matplotlib.transforms import Bbox, IdentityTransform, Affine2D, \
4+
TransformedPatchPath
35
from matplotlib.path import Path
46
import matplotlib._image as _image
57
import numpy as np
@@ -101,9 +103,7 @@ def _get_axes_display_box(self):
101103
Private method, get the bounding box of the child axes in display
102104
coordinates.
103105
"""
104-
return self.__bounding_axes.patch.get_bbox().transformed(
105-
self.__bounding_axes.transAxes
106-
)
106+
return self.__bounding_axes.get_window_extent()
107107

108108
def _get_transfer_transform(self, orig_transform):
109109
"""
@@ -191,6 +191,8 @@ def draw_path(self, gc, path, transform, rgbFace=None):
191191

192192
# Change the clip to the sub-axes box
193193
gc.set_clip_rectangle(bbox)
194+
if(not isinstance(self.__bounding_axes.patch, Rectangle)):
195+
gc.set_clip_path(TransformedPatchPath(self.__bounding_axes.patch))
194196

195197
rgbFace = tuple(rgbFace) if(rgbFace is not None) else None
196198

@@ -219,12 +221,14 @@ def draw_gouraud_triangle(self, gc, points, colors, transform):
219221
gc = self._scale_gc(gc)
220222

221223
gc.set_clip_rectangle(bbox)
224+
if(not isinstance(self.__bounding_axes.patch, Rectangle)):
225+
gc.set_clip_path(TransformedPatchPath(self.__bounding_axes.patch))
222226

223227
self.__renderer.draw_gouraud_triangle(gc, path.vertices, colors,
224228
IdentityTransform())
225229

226230
# Images prove to be especially messy to deal with...
227-
def draw_image(self, gc, x, y, im, transform=None):
231+
def draw_image(self, gc, x, y, im, transform = None):
228232
mag = self.get_image_magnification()
229233
shift_data_transform = self._get_transfer_transform(
230234
IdentityTransform()
@@ -273,6 +277,8 @@ def draw_image(self, gc, x, y, im, transform=None):
273277
gc = self._scale_gc(gc)
274278

275279
gc.set_clip_rectangle(clipped_out_box)
280+
if(not isinstance(self.__bounding_axes.patch, Rectangle)):
281+
gc.set_clip_path(TransformedPatchPath(self.__bounding_axes.patch))
276282

277283
x, y = clipped_out_box.x0, clipped_out_box.y0
278284

matplotview/_view_axes.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import itertools
2-
from typing import Type, List, Optional, Callable
2+
from typing import Type, List, Optional, Callable, Any
33
from matplotlib.axes import Axes
44
from matplotlib.transforms import Bbox
55
import matplotlib.docstring as docstring
@@ -9,7 +9,7 @@
99

1010
DEFAULT_RENDER_DEPTH = 5
1111

12-
class BoundRendererArtist:
12+
class _BoundRendererArtist:
1313
def __init__(
1414
self,
1515
artist: Artist,
@@ -20,13 +20,13 @@ def __init__(
2020
self._renderer = renderer
2121
self._clip_box = clip_box
2222

23-
def __getattribute__(self, item):
23+
def __getattribute__(self, item: str) -> Any:
2424
try:
2525
return super().__getattribute__(item)
2626
except AttributeError:
2727
return self._artist.__getattribute__(item)
2828

29-
def __setattr__(self, key, value):
29+
def __setattr__(self, key: str, value: Any):
3030
try:
3131
super().__setattr__(key, value)
3232
except AttributeError:
@@ -36,8 +36,11 @@ def draw(self, renderer: RendererBase):
3636
# Disable the artist defined clip box, as the artist might be visible
3737
# under the new renderer even if not on screen...
3838
clip_box_orig = self._artist.get_clip_box()
39+
clip_path_orig = self._artist.get_clip_path()
40+
3941
full_extents = self._artist.get_window_extent(self._renderer)
40-
self._artist.set_clip_box(full_extents)
42+
self._artist.set_clip_box(None)
43+
self._artist.set_clip_path(None)
4144

4245
# If we are working with a 3D object, swap out it's axes with
4346
# this zoom axes (swapping out the 3d transform) and reproject it.
@@ -49,16 +52,21 @@ def draw(self, renderer: RendererBase):
4952
if(Bbox.intersection(full_extents, self._clip_box) is not None):
5053
self._artist.draw(self._renderer)
5154

52-
# Re-enable the clip box...
55+
# Re-enable the clip box... and clip path...
5356
self._artist.set_clip_box(clip_box_orig)
57+
self._artist.set_clip_path(clip_path_orig)
5458

55-
def do_3d_projection(self):
59+
def do_3d_projection(self) -> float:
60+
# Get the 3D projection function...
5661
do_3d_projection = getattr(self._artist, "do_3d_projection")
5762

63+
# Intentionally replace the axes of the artist with the view axes,
64+
# as the do_3d_projection pulls the 3D transform (M) from the axes.
65+
# Then reproject, and restore the original axes.
5866
ax = self._artist.axes
59-
self._artist.axes = None
67+
self._artist.axes = None # Set to None first to avoid exception...
6068
self._artist.axes = self._renderer.bounding_axes
61-
res = do_3d_projection()
69+
res = do_3d_projection() # Returns a z-order value...
6270
self._artist.axes = None
6371
self._artist.axes = ax
6472

@@ -195,9 +203,10 @@ def get_children(self) -> List[Artist]:
195203

196204
init_list = super().get_children()
197205
init_list.extend([
198-
BoundRendererArtist(a, mock_renderer, axes_box)
206+
_BoundRendererArtist(a, mock_renderer, axes_box)
199207
for a in itertools.chain(
200-
self.__view_axes._children, self.__view_axes.child_axes
208+
self.__view_axes._children,
209+
self.__view_axes.child_axes
201210
) if(self.__filter_function(a))
202211
])
203212

matplotview/tests/test_inset_zoom.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ def test_3d_view(fig_test, fig_ref):
118118
ax1_test, ax2_test = fig_test.subplots(
119119
1, 2, subplot_kw=dict(projection="3d")
120120
)
121-
ax1_test.plot_surface(X, Y, Z)
121+
ax1_test.plot_surface(X, Y, Z, cmap="plasma")
122122
view(ax2_test, ax1_test)
123+
ax2_test.view_init(elev=80)
123124
ax2_test.set_xlim(-10, 10)
124125
ax2_test.set_ylim(-10, 10)
125126
ax2_test.set_zlim(-2, 2)
@@ -128,8 +129,29 @@ def test_3d_view(fig_test, fig_ref):
128129
ax1_ref, ax2_ref = fig_ref.subplots(
129130
1, 2, subplot_kw=dict(projection="3d")
130131
)
131-
ax1_ref.plot_surface(X, Y, Z)
132-
ax2_ref.plot_surface(X, Y, Z)
132+
ax1_ref.plot_surface(X, Y, Z, cmap="plasma")
133+
ax2_ref.plot_surface(X, Y, Z, cmap="plasma")
134+
ax2_ref.view_init(elev=80)
133135
ax2_ref.set_xlim(-10, 10)
134136
ax2_ref.set_ylim(-10, 10)
135-
ax2_ref.set_zlim(-2, 2)
137+
ax2_ref.set_zlim(-2, 2)
138+
139+
@check_figures_equal()
140+
def test_polar_view(fig_test, fig_ref):
141+
r = np.arange(0, 2, 0.01)
142+
theta = 2 * np.pi * r
143+
144+
# Test Case with polar coordinate system...
145+
ax_t1, ax_t2 = fig_test.subplots(1, 2, subplot_kw=dict(projection="polar"))
146+
ax_t1.plot(theta, r)
147+
ax_t1.set_rmax(2)
148+
view(ax_t2, ax_t1)
149+
ax_t2.set_linescaling(False)
150+
ax_t2.set_rmax(1)
151+
152+
# Reference...
153+
ax_r1, ax_r2 = fig_ref.subplots(1, 2, subplot_kw=dict(projection="polar"))
154+
ax_r1.plot(theta, r)
155+
ax_r1.set_rmax(2)
156+
ax_r2.plot(theta, r)
157+
ax_r2.set_rmax(1)

0 commit comments

Comments
 (0)