@@ -1036,45 +1036,75 @@ def format_zdata(self, z):
1036
1036
val = func (z )
1037
1037
return val
1038
1038
1039
- def format_coord (self , xd , yd ):
1039
+ def format_coord (self , xd , yd , renderer = None ):
1040
1040
"""
1041
1041
Given the 2D view coordinates attempt to guess a 3D coordinate.
1042
1042
Looks for the nearest edge to the point and then assumes that
1043
1043
the point is at the same z location as the nearest point on the edge.
1044
1044
"""
1045
-
1046
1045
if self .M is None :
1047
- return ''
1046
+ coords = ''
1048
1047
1049
- if self .button_pressed in self ._rotate_btn :
1048
+ elif self .button_pressed in self ._rotate_btn :
1050
1049
# ignore xd and yd and display angles instead
1051
1050
norm_elev = art3d ._norm_angle (self .elev )
1052
1051
norm_azim = art3d ._norm_angle (self .azim )
1053
1052
norm_roll = art3d ._norm_angle (self .roll )
1054
- return (f"elevation={ norm_elev :.0f} \N{DEGREE SIGN} , "
1055
- f"azimuth={ norm_azim :.0f} \N{DEGREE SIGN} , "
1056
- f"roll={ norm_roll :.0f} \N{DEGREE SIGN} "
1057
- ).replace ("-" , "\N{MINUS SIGN} " )
1058
-
1059
- # nearest edge
1060
- p0 , p1 = min (self ._tunit_edges (),
1061
- key = lambda edge : proj3d ._line2d_seg_dist (
1062
- (xd , yd ), edge [0 ][:2 ], edge [1 ][:2 ]))
1063
-
1064
- # scale the z value to match
1065
- x0 , y0 , z0 = p0
1066
- x1 , y1 , z1 = p1
1067
- d0 = np .hypot (x0 - xd , y0 - yd )
1068
- d1 = np .hypot (x1 - xd , y1 - yd )
1069
- dt = d0 + d1
1070
- z = d1 / dt * z0 + d0 / dt * z1
1071
-
1072
- x , y , z = proj3d .inv_transform (xd , yd , z , self .M )
1073
-
1074
- xs = self .format_xdata (x )
1075
- ys = self .format_ydata (y )
1076
- zs = self .format_zdata (z )
1077
- return f'x={ xs } , y={ ys } , z={ zs } '
1053
+ coords = (f"elevation={ norm_elev :.0f} \N{DEGREE SIGN} , "
1054
+ f"azimuth={ norm_azim :.0f} \N{DEGREE SIGN} , "
1055
+ f"roll={ norm_roll :.0f} \N{DEGREE SIGN} "
1056
+ ).replace ("-" , "\N{MINUS SIGN} " )
1057
+
1058
+ else :
1059
+ p1 = self ._calc_coord (xd , yd , renderer )
1060
+ xs = self .format_xdata (p1 [0 ])
1061
+ ys = self .format_ydata (p1 [1 ])
1062
+ zs = self .format_zdata (p1 [2 ])
1063
+ coords = f'x={ xs } , y={ ys } , z={ zs } '
1064
+
1065
+ return coords
1066
+
1067
+ def _get_camera_loc (self ):
1068
+ """
1069
+ Returns the current camera location in data coordinates.
1070
+ """
1071
+ cx , cy , cz , dx , dy , dz = self ._get_w_centers_ranges ()
1072
+ c = np .array ([cx , cy , cz ])
1073
+ r = np .array ([dx , dy , dz ])
1074
+ eye = c + self ._view_w * self ._dist * r / self ._box_aspect
1075
+ return eye
1076
+
1077
+ def _calc_coord (self , xd , yd , renderer = None ):
1078
+ """
1079
+ Given the 2D view coordinates, find the point on the nearest axis pane
1080
+ that lies directly below those coordinates.
1081
+ """
1082
+ # convert to data coordinates
1083
+ p1 = np .array (proj3d .inv_transform (xd , yd , - 1 , self .M ))
1084
+
1085
+ vec = self ._get_camera_loc () - p1
1086
+
1087
+ if self ._focal_length == np .inf :
1088
+ vec = self ._view_w
1089
+
1090
+ # Get the pane locations for each of the axes
1091
+ pane_locs = []
1092
+ for axis in self ._axis_map .values ():
1093
+ xys , loc = axis .active_pane (renderer )
1094
+ pane_locs .append (loc )
1095
+
1096
+ # Find the distance to the nearest pane
1097
+ scales = np .zeros (3 )
1098
+ for i in range (3 ):
1099
+ if vec [i ] == 0 :
1100
+ scales [i ] = np .inf
1101
+ else :
1102
+ scales [i ] = (p1 [i ] - pane_locs [i ]) / vec [i ]
1103
+ scale = scales [np .argmin (abs (scales ))]
1104
+
1105
+ # Calculate the point on the closest pane
1106
+ p2 = p1 - scale * vec
1107
+ return p2
1078
1108
1079
1109
def _on_move (self , event ):
1080
1110
"""
@@ -1296,21 +1326,27 @@ def _scale_axis_limits(self, scale_x, scale_y, scale_z):
1296
1326
scale_z : float
1297
1327
Scale factor for the z data axis.
1298
1328
"""
1299
- # Get the axis limits and centers
1329
+ # Get the axis centers and ranges
1330
+ cx , cy , cz , dx , dy , dz = self ._get_w_centers_ranges ()
1331
+
1332
+ # Set the scaled axis limits
1333
+ self .set_xlim3d (cx - dx * scale_x / 2 , cx + dx * scale_x / 2 )
1334
+ self .set_ylim3d (cy - dy * scale_y / 2 , cy + dy * scale_y / 2 )
1335
+ self .set_zlim3d (cz - dz * scale_z / 2 , cz + dz * scale_z / 2 )
1336
+
1337
+ def _get_w_centers_ranges (self ):
1338
+ """Get 3D world centers and axis ranges."""
1339
+ # Calculate center of axis limits
1300
1340
minx , maxx , miny , maxy , minz , maxz = self .get_w_lims ()
1301
1341
cx = (maxx + minx )/ 2
1302
1342
cy = (maxy + miny )/ 2
1303
1343
cz = (maxz + minz )/ 2
1304
1344
1305
- # Scale the data range
1306
- dx = (maxx - minx )* scale_x
1307
- dy = (maxy - miny )* scale_y
1308
- dz = (maxz - minz )* scale_z
1309
-
1310
- # Set the scaled axis limits
1311
- self .set_xlim3d (cx - dx / 2 , cx + dx / 2 )
1312
- self .set_ylim3d (cy - dy / 2 , cy + dy / 2 )
1313
- self .set_zlim3d (cz - dz / 2 , cz + dz / 2 )
1345
+ # Calculate range of axis limits
1346
+ dx = (maxx - minx )
1347
+ dy = (maxy - miny )
1348
+ dz = (maxz - minz )
1349
+ return cx , cy , cz , dx , dy , dz
1314
1350
1315
1351
def set_zlabel (self , zlabel , fontdict = None , labelpad = None , ** kwargs ):
1316
1352
"""
0 commit comments