@@ -1088,17 +1088,9 @@ def _on_move(self, event):
1088
1088
1089
1089
# Zoom
1090
1090
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 )
1102
1094
1103
1095
# Store the event coordinates for the next time through.
1104
1096
self .sx , self .sy = x , y
@@ -1143,63 +1135,31 @@ def drag_pan(self, button, key, x, y):
1143
1135
1144
1136
def _set_view_from_bbox (self , bbox , direction = 'in' ,
1145
1137
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
1147
1142
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 )
1197
1158
1198
1159
def _prepare_view_from_bbox (self , bbox , direction = 'in' ,
1199
1160
mode = None , twinx = False , twiny = False ):
1200
1161
"""
1201
1162
Helper function to prepare the new bounds from a bbox.
1202
-
1203
1163
This helper function returns the new x and y bounds from the zoom
1204
1164
bbox. This a convenience method to abstract the bbox logic
1205
1165
out of the base setter.
@@ -1232,49 +1192,21 @@ def _prepare_view_from_bbox(self, bbox, direction='in',
1232
1192
"of length 3 or 4. Ignoring the view change." )
1233
1193
return
1234
1194
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 ()
1278
1210
1279
1211
def set_zlabel (self , zlabel , fontdict = None , labelpad = None , ** kwargs ):
1280
1212
"""
0 commit comments