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 9fd9c66

Browse filesBrowse files
Rewrite zooming with bounding box
1 parent d161de3 commit 9fd9c66
Copy full SHA for 9fd9c66

File tree

Expand file treeCollapse file tree

1 file changed

+37
-105
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+37
-105
lines changed

‎lib/mpl_toolkits/mplot3d/axes3d.py

Copy file name to clipboardExpand all lines: lib/mpl_toolkits/mplot3d/axes3d.py
+37-105Lines changed: 37 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,17 +1088,9 @@ def _on_move(self, event):
10881088

10891089
# Zoom
10901090
elif self.button_pressed in self._zoom_btn:
1091-
# zoom view
1092-
# hmmm..this needs some help from clipping....
1093-
minx, maxx, miny, maxy, minz, maxz = self.get_w_lims()
1094-
df = 1-((h - dy)/h)
1095-
dx = (maxx-minx)*df
1096-
dy = (maxy-miny)*df
1097-
dz = (maxz-minz)*df
1098-
self.set_xlim3d(minx - dx, maxx + dx)
1099-
self.set_ylim3d(miny - dy, maxy + dy)
1100-
self.set_zlim3d(minz - dz, maxz + dz)
1101-
self.get_proj()
1091+
# zoom view (dragging down zooms in)
1092+
scale = h/(h - dy)
1093+
self._zoom_data_limits(scale)
11021094

11031095
# Store the event coordinates for the next time through.
11041096
self.sx, self.sy = x, y
@@ -1143,63 +1135,31 @@ def drag_pan(self, button, key, x, y):
11431135

11441136
def _set_view_from_bbox(self, bbox, direction='in',
11451137
mode=None, twinx=False, twiny=False):
1146-
# docstring inherited
1138+
# Move the center of the view to the center of the bbox
1139+
(start_x, start_y, stop_x, stop_y) = self._prepare_view_from_bbox(bbox)
1140+
zoom_center_x = (start_x + stop_x)/2
1141+
zoom_center_y = (start_y + stop_y)/2
11471142

1148-
# bbox is (start_x, start_y, event.x, event.y) in screen coords
1149-
# _prepare_view_from_bbox will give us back new *data* coords
1150-
# (in the 2D transform space, not 3D world coords)
1151-
new_xbound, new_ybound = self._prepare_view_from_bbox(
1152-
bbox, direction=direction, mode=mode, twinx=twinx, twiny=twiny)
1153-
# We need to get the Zoom bbox limits relative to the Axes limits
1154-
# 1) Axes bottom-left -> Zoom box bottom-left
1155-
# 2) Axes top-right -> Zoom box top-right
1156-
axes_to_data_trans = self.transAxes + self.transData.inverted()
1157-
axes_data_bbox = axes_to_data_trans.transform([(0, 0), (1, 1)])
1158-
# dx, dy gives us the vector difference from the axes to the
1159-
dx1, dy1 = (axes_data_bbox[0][0] - new_xbound[0],
1160-
axes_data_bbox[0][1] - new_ybound[0])
1161-
dx2, dy2 = (axes_data_bbox[1][0] - new_xbound[1],
1162-
axes_data_bbox[1][1] - new_ybound[1])
1163-
1164-
def data_2d_to_world_3d(dx, dy):
1165-
# Takes the vector (dx, dy) in transData coords and
1166-
# transforms that to each of the 3 world data coords
1167-
# (x, y, z) for calculating the offset
1168-
w = self._pseudo_w
1169-
h = self._pseudo_h
1170-
1171-
dx = 1 - ((w - dx) / w)
1172-
dy = 1 - ((h - dy) / h)
1173-
elev = np.deg2rad(self.elev)
1174-
azim = np.deg2rad(self.azim)
1175-
# project xv, yv, zv -> xw, yw, zw
1176-
dxx = (dy * np.sin(elev)
1177-
* np.cos(azim) + dx * np.sin(azim))
1178-
dyy = (-dx * np.cos(azim)
1179-
+ dy * np.sin(elev) * np.sin(azim))
1180-
dzz = (-dy * np.cos(elev))
1181-
return dxx, dyy, dzz
1182-
1183-
# These are the amounts to bring the projection in or out by from
1184-
# each side (1 left, 2 right) because we aren't necessarily zooming
1185-
# into the center of the projection.
1186-
dxx1, dyy1, dzz1 = data_2d_to_world_3d(dx1, dy1)
1187-
dxx2, dyy2, dzz2 = data_2d_to_world_3d(dx2, dy2)
1188-
# update the min and max limits of the world
1189-
minx, maxx, miny, maxy, minz, maxz = self.get_w_lims()
1190-
self.set_xlim3d(minx + dxx1 * (maxx - minx),
1191-
maxx + dxx2 * (maxx - minx))
1192-
self.set_ylim3d(miny + dyy1 * (maxy - miny),
1193-
maxy + dyy2 * (maxy - miny))
1194-
self.set_zlim3d(minz + dzz1 * (maxz - minz),
1195-
maxz + dzz2 * (maxz - minz))
1196-
self.get_proj()
1143+
ax_center_x = (self.bbox.max[0] + self.bbox.min[0])/2
1144+
ax_center_y = (self.bbox.max[1] + self.bbox.min[1])/2
1145+
1146+
self.start_pan(zoom_center_x, zoom_center_y, 2)
1147+
self.drag_pan(2, None, ax_center_x, ax_center_y)
1148+
self.end_pan()
1149+
1150+
# Calculate zoom level
1151+
scale_x = abs((start_x - stop_x)/(self.bbox.max[0] - self.bbox.min[0]))
1152+
scale_y = abs((start_y - stop_y)/(self.bbox.max[1] - self.bbox.min[1]))
1153+
scale = max(scale_x, scale_y)
1154+
if direction == 'out':
1155+
scale = 1 / scale
1156+
1157+
self._zoom_data_limits(scale)
11971158

11981159
def _prepare_view_from_bbox(self, bbox, direction='in',
11991160
mode=None, twinx=False, twiny=False):
12001161
"""
12011162
Helper function to prepare the new bounds from a bbox.
1202-
12031163
This helper function returns the new x and y bounds from the zoom
12041164
bbox. This a convenience method to abstract the bbox logic
12051165
out of the base setter.
@@ -1232,49 +1192,21 @@ def _prepare_view_from_bbox(self, bbox, direction='in',
12321192
"of length 3 or 4. Ignoring the view change.")
12331193
return
12341194

1235-
# Original limits
1236-
# Can't use get_x/y bounds because those aren't in 2D space
1237-
pseudo_bbox = self.transLimits.inverted().transform([(0, 0), (1, 1)])
1238-
(xmin0, ymin0), (xmax0, ymax0) = pseudo_bbox
1239-
# The zoom box in screen coords.
1240-
startx, starty, stopx, stopy = bbox
1241-
# Convert to data coords.
1242-
(startx, starty), (stopx, stopy) = self.transData.inverted().transform(
1243-
[(startx, starty), (stopx, stopy)])
1244-
# Clip to axes limits.
1245-
xmin, xmax = np.clip(sorted([startx, stopx]), xmin0, xmax0)
1246-
ymin, ymax = np.clip(sorted([starty, stopy]), ymin0, ymax0)
1247-
# Don't double-zoom twinned axes or if zooming only the other axis.
1248-
if twinx or mode == "y":
1249-
xmin, xmax = xmin0, xmax0
1250-
if twiny or mode == "x":
1251-
ymin, ymax = ymin0, ymax0
1252-
1253-
if direction == "in":
1254-
new_xbound = xmin, xmax
1255-
new_ybound = ymin, ymax
1256-
1257-
elif direction == "out":
1258-
x_trf = self.xaxis.get_transform()
1259-
sxmin0, sxmax0, sxmin, sxmax = x_trf.transform(
1260-
[xmin0, xmax0, xmin, xmax]) # To screen space.
1261-
factor = (sxmax0 - sxmin0) / (sxmax - sxmin) # Unzoom factor.
1262-
# Move original bounds away by
1263-
# (factor) x (distance between unzoom box and Axes bbox).
1264-
sxmin1 = sxmin0 - factor * (sxmin - sxmin0)
1265-
sxmax1 = sxmax0 + factor * (sxmax0 - sxmax)
1266-
# And back to data space.
1267-
new_xbound = x_trf.inverted().transform([sxmin1, sxmax1])
1268-
1269-
y_trf = self.yaxis.get_transform()
1270-
symin0, symax0, symin, symax = y_trf.transform(
1271-
[ymin0, ymax0, ymin, ymax])
1272-
factor = (symax0 - symin0) / (symax - symin)
1273-
symin1 = symin0 - factor * (symin - symin0)
1274-
symax1 = symax0 + factor * (symax0 - symax)
1275-
new_ybound = y_trf.inverted().transform([symin1, symax1])
1276-
1277-
return new_xbound, new_ybound
1195+
return bbox
1196+
1197+
def _zoom_data_limits(self, scale):
1198+
# hmmm..this needs some help from clipping....
1199+
minx, maxx, miny, maxy, minz, maxz = self.get_w_lims()
1200+
cx = (maxx + minx)/2
1201+
cy = (maxy + miny)/2
1202+
cz = (maxz + minz)/2
1203+
dx = (maxx - minx)*scale/2
1204+
dy = (maxy - miny)*scale/2
1205+
dz = (maxz - minz)*scale/2
1206+
self.set_xlim3d(cx - dx, cx + dx)
1207+
self.set_ylim3d(cy - dy, cy + dy)
1208+
self.set_zlim3d(cz - dz, cz + dz)
1209+
self.get_proj()
12781210

12791211
def set_zlabel(self, zlabel, fontdict=None, labelpad=None, **kwargs):
12801212
"""

0 commit comments

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