Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7e582b1

Browse files
committed
ENH: Allow custom Axes to implement zoom-to-rect.
The main benefit here will be PolarAxes, but there might be other custom Axes that find this useful.
1 parent eb80fe5 commit 7e582b1

File tree

3 files changed

+147
-176
lines changed

3 files changed

+147
-176
lines changed

lib/matplotlib/axes/_base.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,6 +3166,133 @@ def _set_view(self, view):
31663166
self.set_xlim((xmin, xmax))
31673167
self.set_ylim((ymin, ymax))
31683168

3169+
def _set_view_from_bbox(self, bbox, original_view, direction='in',
3170+
mode=None, twinx=False, twiny=False):
3171+
"""
3172+
Update view from a selection bbox.
3173+
3174+
.. note::
3175+
3176+
Intended to be overridden by new projection types, but if not, the
3177+
default implementation sets the view limits to the bbox directly.
3178+
3179+
Parameters
3180+
----------
3181+
3182+
bbox : tuple
3183+
The selected bounding box limits, in *display* coordinates.
3184+
3185+
original_view : any
3186+
A view saved from before initiating the selection, the result of
3187+
calling :meth:`_get_view`.
3188+
3189+
direction : str
3190+
The direction to apply the bounding box.
3191+
* `'in'` - The bounding box describes the view directly, i.e.,
3192+
it zooms in.
3193+
* `'out'` - The bounding box describes the size to make the
3194+
existing view, i.e., it zooms out.
3195+
3196+
mode : str or None
3197+
The selection mode, whether to apply the bounding box in only the
3198+
`'x'` direction, `'y'` direction or both (`None`).
3199+
3200+
twinx : bool
3201+
Whether this axis is twinned in the *x*-direction.
3202+
3203+
twiny : bool
3204+
Whether this axis is twinned in the *y*-direction.
3205+
"""
3206+
3207+
lastx, lasty, x, y = bbox
3208+
3209+
x0, y0, x1, y1 = original_view
3210+
3211+
# zoom to rect
3212+
inverse = self.transData.inverted()
3213+
lastx, lasty = inverse.transform_point((lastx, lasty))
3214+
x, y = inverse.transform_point((x, y))
3215+
Xmin, Xmax = self.get_xlim()
3216+
Ymin, Ymax = self.get_ylim()
3217+
3218+
if twinx:
3219+
x0, x1 = Xmin, Xmax
3220+
else:
3221+
if Xmin < Xmax:
3222+
if x < lastx:
3223+
x0, x1 = x, lastx
3224+
else:
3225+
x0, x1 = lastx, x
3226+
if x0 < Xmin:
3227+
x0 = Xmin
3228+
if x1 > Xmax:
3229+
x1 = Xmax
3230+
else:
3231+
if x > lastx:
3232+
x0, x1 = x, lastx
3233+
else:
3234+
x0, x1 = lastx, x
3235+
if x0 > Xmin:
3236+
x0 = Xmin
3237+
if x1 < Xmax:
3238+
x1 = Xmax
3239+
3240+
if twiny:
3241+
y0, y1 = Ymin, Ymax
3242+
else:
3243+
if Ymin < Ymax:
3244+
if y < lasty:
3245+
y0, y1 = y, lasty
3246+
else:
3247+
y0, y1 = lasty, y
3248+
if y0 < Ymin:
3249+
y0 = Ymin
3250+
if y1 > Ymax:
3251+
y1 = Ymax
3252+
else:
3253+
if y > lasty:
3254+
y0, y1 = y, lasty
3255+
else:
3256+
y0, y1 = lasty, y
3257+
if y0 > Ymin:
3258+
y0 = Ymin
3259+
if y1 < Ymax:
3260+
y1 = Ymax
3261+
3262+
if direction == 'in':
3263+
if mode == 'x':
3264+
self.set_xlim((x0, x1))
3265+
elif mode == 'y':
3266+
self.set_ylim((y0, y1))
3267+
else:
3268+
self.set_xlim((x0, x1))
3269+
self.set_ylim((y0, y1))
3270+
elif direction == 'out':
3271+
if self.get_xscale() == 'log':
3272+
alpha = np.log(Xmax / Xmin) / np.log(x1 / x0)
3273+
rx1 = pow(Xmin / x0, alpha) * Xmin
3274+
rx2 = pow(Xmax / x0, alpha) * Xmin
3275+
else:
3276+
alpha = (Xmax - Xmin) / (x1 - x0)
3277+
rx1 = alpha * (Xmin - x0) + Xmin
3278+
rx2 = alpha * (Xmax - x0) + Xmin
3279+
if self.get_yscale() == 'log':
3280+
alpha = np.log(Ymax / Ymin) / np.log(y1 / y0)
3281+
ry1 = pow(Ymin / y0, alpha) * Ymin
3282+
ry2 = pow(Ymax / y0, alpha) * Ymin
3283+
else:
3284+
alpha = (Ymax - Ymin) / (y1 - y0)
3285+
ry1 = alpha * (Ymin - y0) + Ymin
3286+
ry2 = alpha * (Ymax - y0) + Ymin
3287+
3288+
if mode == 'x':
3289+
self.set_xlim((rx1, rx2))
3290+
elif mode == 'y':
3291+
self.set_ylim((ry1, ry2))
3292+
else:
3293+
self.set_xlim((rx1, rx2))
3294+
self.set_ylim((ry1, ry2))
3295+
31693296
def start_pan(self, x, y, button):
31703297
"""
31713298
Called when a pan operation has started.

lib/matplotlib/backend_bases.py

Lines changed: 10 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2925,8 +2925,7 @@ def press_zoom(self, event):
29252925
for i, a in enumerate(self.canvas.figure.get_axes()):
29262926
if (x is not None and y is not None and a.in_axes(event) and
29272927
a.get_navigate() and a.can_zoom()):
2928-
self._xypress.append((x, y, a, i, a.viewLim.frozen(),
2929-
a.transData.frozen()))
2928+
self._xypress.append((x, y, a, i, a._get_view()))
29302929

29312930
id1 = self.canvas.mpl_connect('motion_notify_event', self.drag_zoom)
29322931
id2 = self.canvas.mpl_connect('key_press_event',
@@ -2997,7 +2996,7 @@ def drag_zoom(self, event):
29972996

29982997
if self._xypress:
29992998
x, y = event.x, event.y
3000-
lastx, lasty, a, ind, lim, trans = self._xypress[0]
2999+
lastx, lasty, a, ind, view = self._xypress[0]
30013000

30023001
# adjust x, last, y, last
30033002
x1, y1, x2, y2 = a.bbox.extents
@@ -3026,23 +3025,14 @@ def release_zoom(self, event):
30263025

30273026
for cur_xypress in self._xypress:
30283027
x, y = event.x, event.y
3029-
lastx, lasty, a, ind, lim, trans = cur_xypress
3028+
lastx, lasty, a, ind, view = cur_xypress
30303029
# ignore singular clicks - 5 pixels is a threshold
30313030
if abs(x - lastx) < 5 or abs(y - lasty) < 5:
30323031
self._xypress = None
30333032
self.release(event)
30343033
self.draw()
30353034
return
30363035

3037-
x0, y0, x1, y1 = lim.extents
3038-
3039-
# zoom to rect
3040-
inverse = a.transData.inverted()
3041-
lastx, lasty = inverse.transform_point((lastx, lasty))
3042-
x, y = inverse.transform_point((x, y))
3043-
Xmin, Xmax = a.get_xlim()
3044-
Ymin, Ymax = a.get_ylim()
3045-
30463036
# detect twinx,y axes and avoid double zooming
30473037
twinx, twiny = False, False
30483038
if last_a:
@@ -3053,83 +3043,15 @@ def release_zoom(self, event):
30533043
twiny = True
30543044
last_a.append(a)
30553045

3056-
if twinx:
3057-
x0, x1 = Xmin, Xmax
3058-
else:
3059-
if Xmin < Xmax:
3060-
if x < lastx:
3061-
x0, x1 = x, lastx
3062-
else:
3063-
x0, x1 = lastx, x
3064-
if x0 < Xmin:
3065-
x0 = Xmin
3066-
if x1 > Xmax:
3067-
x1 = Xmax
3068-
else:
3069-
if x > lastx:
3070-
x0, x1 = x, lastx
3071-
else:
3072-
x0, x1 = lastx, x
3073-
if x0 > Xmin:
3074-
x0 = Xmin
3075-
if x1 < Xmax:
3076-
x1 = Xmax
3077-
3078-
if twiny:
3079-
y0, y1 = Ymin, Ymax
3080-
else:
3081-
if Ymin < Ymax:
3082-
if y < lasty:
3083-
y0, y1 = y, lasty
3084-
else:
3085-
y0, y1 = lasty, y
3086-
if y0 < Ymin:
3087-
y0 = Ymin
3088-
if y1 > Ymax:
3089-
y1 = Ymax
3090-
else:
3091-
if y > lasty:
3092-
y0, y1 = y, lasty
3093-
else:
3094-
y0, y1 = lasty, y
3095-
if y0 > Ymin:
3096-
y0 = Ymin
3097-
if y1 < Ymax:
3098-
y1 = Ymax
3099-
31003046
if self._button_pressed == 1:
3101-
if self._zoom_mode == "x":
3102-
a.set_xlim((x0, x1))
3103-
elif self._zoom_mode == "y":
3104-
a.set_ylim((y0, y1))
3105-
else:
3106-
a.set_xlim((x0, x1))
3107-
a.set_ylim((y0, y1))
3047+
direction = 'in'
31083048
elif self._button_pressed == 3:
3109-
if a.get_xscale() == 'log':
3110-
alpha = np.log(Xmax / Xmin) / np.log(x1 / x0)
3111-
rx1 = pow(Xmin / x0, alpha) * Xmin
3112-
rx2 = pow(Xmax / x0, alpha) * Xmin
3113-
else:
3114-
alpha = (Xmax - Xmin) / (x1 - x0)
3115-
rx1 = alpha * (Xmin - x0) + Xmin
3116-
rx2 = alpha * (Xmax - x0) + Xmin
3117-
if a.get_yscale() == 'log':
3118-
alpha = np.log(Ymax / Ymin) / np.log(y1 / y0)
3119-
ry1 = pow(Ymin / y0, alpha) * Ymin
3120-
ry2 = pow(Ymax / y0, alpha) * Ymin
3121-
else:
3122-
alpha = (Ymax - Ymin) / (y1 - y0)
3123-
ry1 = alpha * (Ymin - y0) + Ymin
3124-
ry2 = alpha * (Ymax - y0) + Ymin
3125-
3126-
if self._zoom_mode == "x":
3127-
a.set_xlim((rx1, rx2))
3128-
elif self._zoom_mode == "y":
3129-
a.set_ylim((ry1, ry2))
3130-
else:
3131-
a.set_xlim((rx1, rx2))
3132-
a.set_ylim((ry1, ry2))
3049+
direction = 'out'
3050+
else:
3051+
continue
3052+
3053+
a._set_view_from_bbox((lastx, lasty, x, y), view, direction,
3054+
self._zoom_mode, twinx, twiny)
31333055

31343056
self.draw()
31353057
self._xypress = None

0 commit comments

Comments
 (0)